summaryrefslogtreecommitdiff
path: root/RC9/qpid/java/common
diff options
context:
space:
mode:
Diffstat (limited to 'RC9/qpid/java/common')
-rw-r--r--RC9/qpid/java/common/Composite.tpl336
-rw-r--r--RC9/qpid/java/common/Constant.tpl35
-rw-r--r--RC9/qpid/java/common/Enum.tpl57
-rw-r--r--RC9/qpid/java/common/Invoker.tpl73
-rw-r--r--RC9/qpid/java/common/MethodDelegate.tpl37
-rw-r--r--RC9/qpid/java/common/Option.tpl41
-rw-r--r--RC9/qpid/java/common/StructFactory.tpl60
-rw-r--r--RC9/qpid/java/common/Type.tpl84
-rwxr-xr-xRC9/qpid/java/common/bin/qpid-run264
-rw-r--r--RC9/qpid/java/common/build.xml104
-rwxr-xr-xRC9/qpid/java/common/codegen89
-rw-r--r--RC9/qpid/java/common/etc/qpid-run.conf25
-rw-r--r--RC9/qpid/java/common/etc/qpid-run.conf.dev26
-rw-r--r--RC9/qpid/java/common/genutil.py255
-rw-r--r--RC9/qpid/java/common/protocol-version.xml70
-rw-r--r--RC9/qpid/java/common/readme.txt4
-rwxr-xr-xRC9/qpid/java/common/src/main/java/common.bnd6
-rw-r--r--RC9/qpid/java/common/src/main/java/log4j.properties31
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java467
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java227
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java351
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java48
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java272
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java197
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java440
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java547
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java486
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java67
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java1026
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java240
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java488
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java151
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java41
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelException.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java44
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java70
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java65
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQException.java122
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java45
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java54
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java43
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java50
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetails.java138
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java264
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java54
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ErrorCode.java123
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidConfig.java111
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidException.java58
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/SecurityHelper.java71
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/SerialException.java40
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyBroker.java188
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyClient.java106
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyExchange.java154
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/api/Message.java126
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java77
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java271
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java66
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java61
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java52
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java190
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/Configured.java44
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java43
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java164
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java250
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java65
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java40
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java63
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java120
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java61
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java125
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java47
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java83
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java45
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java96
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java30
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java90
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java41
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java776
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java31
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java795
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java48
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java116
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java834
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java31
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java81
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java78
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/Content.java26
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java121
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java48
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java131
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java50
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java60
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java50
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java35
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java1033
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java1187
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java79
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java31
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java195
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java98
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java198
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java47
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java32
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java32
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java32
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java209
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java172
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java209
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java151
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java155
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java192
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java491
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java432
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java27
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java102
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java172
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java236
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java95
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java70
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java43
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java64
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java53
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java28
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java94
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java80
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java44
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java60
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java105
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java62
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java195
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binary.java145
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binding.java36
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java138
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java505
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java98
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java70
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Echo.java71
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Field.java83
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Future.java37
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Header.java92
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Method.java180
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.java40
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.java83
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.java40
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java116
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java62
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Range.java125
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java147
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Receiver.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Result.java30
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sender.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SenderException.java52
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java154
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java799
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java49
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java149
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionException.java61
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java40
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sink.java136
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Struct.java142
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportException.java51
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java470
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java620
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java144
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java323
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java283
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java44
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java282
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java226
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java83
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java239
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Frame.java151
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java204
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java42
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java34
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java130
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java92
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java145
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java286
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java229
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java274
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java81
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java140
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java121
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java179
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java235
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java97
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java130
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java63
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java236
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java466
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLHelper.java173
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java97
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java689
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java258
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java70
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java38
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java316
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java43
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java75
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java200
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.java39
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Serial.java108
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Strings.java94
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java36
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDs.java59
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java34
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java122
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java834
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java128
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java35
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.java50
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java50
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java52
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java48
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java74
-rw-r--r--RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java51
-rw-r--r--RC9/qpid/java/common/src/main/resources/org/apache/qpid/ssl/qpid.certbin0 -> 756 bytes
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java396
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java157
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java106
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java109
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java188
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java960
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java111
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/session/TestSession.java277
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java385
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/GenTest.java44
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java238
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java554
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java522
-rw-r--r--RC9/qpid/java/common/src/test/java/org/apache/qpid/util/SerialTest.java82
-rw-r--r--RC9/qpid/java/common/templates/method/MethodBodyInterface.vm62
-rw-r--r--RC9/qpid/java/common/templates/method/version/MethodBodyClass.vm209
-rw-r--r--RC9/qpid/java/common/templates/model/ClientMethodDispatcherInterface.vm56
-rw-r--r--RC9/qpid/java/common/templates/model/MethodDispatcherInterface.vm39
-rw-r--r--RC9/qpid/java/common/templates/model/MethodRegistryClass.vm104
-rw-r--r--RC9/qpid/java/common/templates/model/ProtocolVersionListClass.vm178
-rw-r--r--RC9/qpid/java/common/templates/model/ServerMethodDispatcherInterface.vm56
-rw-r--r--RC9/qpid/java/common/templates/model/version/AmqpConstantsClass.vm37
-rw-r--r--RC9/qpid/java/common/templates/model/version/ClientMethodDispatcherInterface.vm55
-rw-r--r--RC9/qpid/java/common/templates/model/version/MethodDispatcherInterface.vm43
-rw-r--r--RC9/qpid/java/common/templates/model/version/MethodRegistryClass.vm193
-rw-r--r--RC9/qpid/java/common/templates/model/version/ServerMethodDispatcherInterface.vm55
-rw-r--r--RC9/qpid/java/common/templating.py119
260 files changed, 40195 insertions, 0 deletions
diff --git a/RC9/qpid/java/common/Composite.tpl b/RC9/qpid/java/common/Composite.tpl
new file mode 100644
index 0000000000..17cf846d8c
--- /dev/null
+++ b/RC9/qpid/java/common/Composite.tpl
@@ -0,0 +1,336 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.transport.codec.Decoder;
+import org.apache.qpid.transport.codec.Encodable;
+import org.apache.qpid.transport.codec.Encoder;
+
+import org.apache.qpid.transport.network.Frame;
+
+import org.apache.qpid.util.Strings;
+
+
+${
+from genutil import *
+
+cls = klass(type)["@name"]
+
+segments = type["segments"]
+
+if type.name in ("control", "command"):
+ base = "Method"
+ size = 0
+ pack = 2
+ if segments:
+ payload = "true"
+ else:
+ payload = "false"
+ if type.name == "control" and cls == "connection":
+ track = "Frame.L1"
+ elif cls == "session" and type["@name"] in ("attach", "attached", "detach", "detached"):
+ track = "Frame.L2"
+ elif type.name == "command":
+ track = "Frame.L4"
+ else:
+ track = "Frame.L3"
+else:
+ base = "Struct"
+ size = type["@size"]
+ pack = num(type["@pack"])
+ payload = "false"
+ track = "-1"
+
+PACK_TYPES = {
+ 1: "byte",
+ 2: "short",
+ 4: "int"
+}
+
+typecode = code(type)
+}
+
+public final class $name extends $base {
+
+ public static final int TYPE = $typecode;
+
+ public final int getStructType() {
+ return TYPE;
+ }
+
+ public final int getSizeWidth() {
+ return $size;
+ }
+
+ public final int getPackWidth() {
+ return $pack;
+ }
+
+ public final boolean hasPayload() {
+ return $payload;
+ }
+
+ public final byte getEncodedTrack() {
+ return $track;
+ }
+
+${
+
+if pack > 0:
+ out(" private $(PACK_TYPES[pack]) packing_flags = 0;\n");
+
+fields = get_fields(type)
+params = get_parameters(type, fields)
+options = get_options(fields)
+
+for f in fields:
+ if not f.empty:
+ out(" private $(f.type) $(f.name);\n")
+
+if segments:
+ out(" private Header header;\n")
+ out(" private ByteBuffer body;\n")
+}
+
+${
+if fields:
+ out(" public $name() {}\n")
+}
+
+ public $name($(", ".join(params))) {
+${
+for f in fields:
+ if f.option: continue
+ out(" $(f.set)($(f.name));\n")
+
+if segments:
+ out(" setHeader(header);\n")
+ out(" setBody(body);\n")
+
+if options or base == "Method":
+ out("""
+ for (int i=0; i < _options.length; i++) {
+ switch (_options[i]) {
+""")
+
+ for f in options:
+ out(" case $(f.option): packing_flags |= $(f.flag_mask(pack)); break;\n")
+
+ if base == "Method":
+ out(""" case SYNC: this.setSync(true); break;
+ case BATCH: this.setBatch(true); break;
+""")
+ out(""" case NONE: break;
+ default: throw new IllegalArgumentException("invalid option: " + _options[i]);
+ }
+ }
+""")
+}
+ }
+
+${
+
+if base == "Method":
+ out(""" public <C> void dispatch(C context, MethodDelegate<C> delegate) {
+ delegate.$(dromedary(name))(context, this);
+ }""")
+}
+
+${
+for f in fields:
+ if pack > 0:
+ out("""
+ public final boolean $(f.has)() {
+ return (packing_flags & $(f.flag_mask(pack))) != 0;
+ }
+
+ public final $name $(f.clear)() {
+ packing_flags &= ~$(f.flag_mask(pack));
+${
+if not f.empty:
+ out(" this.$(f.name) = $(f.default);")
+}
+ this.dirty = true;
+ return this;
+ }
+""")
+
+ out("""
+ public final $(f.type) $(f.get)() {
+${
+if f.empty:
+ out(" return $(f.has)();")
+else:
+ out(" return $(f.name);")
+}
+ }
+
+ public final $name $(f.set)($(f.type) value) {
+${
+if not f.empty:
+ out(" this.$(f.name) = value;")
+}
+${
+if pack > 0:
+ if f.empty:
+ out(" if (value)\\n")
+ out(" packing_flags |= $(f.flag_mask(pack));\\n")
+ out(" else\\n")
+ out(" packing_flags &= ~$(f.flag_mask(pack));")
+ else:
+ out(" packing_flags |= $(f.flag_mask(pack));")
+}
+ this.dirty = true;
+ return this;
+ }
+
+ public final $name $(f.name)($(f.type) value) {
+ return $(f.set)(value);
+ }
+""")
+}
+
+${
+if segments:
+ out(""" public final Header getHeader() {
+ return this.header;
+ }
+
+ public final void setHeader(Header header) {
+ this.header = header;
+ }
+
+ public final $name header(Header header) {
+ setHeader(header);
+ return this;
+ }
+
+ public final ByteBuffer getBody() {
+ if (this.body == null)
+ {
+ return null;
+ }
+ else
+ {
+ return this.body.slice();
+ }
+ }
+
+ public final void setBody(ByteBuffer body) {
+ this.body = body;
+ }
+
+ public final $name body(ByteBuffer body)
+ {
+ setBody(body);
+ return this;
+ }
+
+ public final byte[] getBodyBytes() {
+ ByteBuffer buf = getBody();
+ byte[] bytes = new byte[buf.remaining()];
+ buf.get(bytes);
+ return bytes;
+ }
+
+ public final void setBody(byte[] body)
+ {
+ setBody(ByteBuffer.wrap(body));
+ }
+
+ public final String getBodyString() {
+ return Strings.fromUTF8(getBodyBytes());
+ }
+
+ public final void setBody(String body) {
+ setBody(Strings.toUTF8(body));
+ }
+""")
+}
+
+ public void write(Encoder enc)
+ {
+${
+if pack > 0:
+ out(" enc.writeUint%s(packing_flags);\n" % (pack*8));
+
+for f in fields:
+ if f.empty:
+ continue
+ if pack > 0:
+ out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ")
+ pre = ""
+ post = ""
+ if f.type_node.name == "struct":
+ pre = "%s.TYPE, " % cname(f.type_node)
+ elif f.type_node.name == "domain":
+ post = ".getValue()"
+ out(" enc.write$(f.coder)($(pre)this.$(f.name)$(post));\n")
+}
+ }
+
+ public void read(Decoder dec)
+ {
+${
+if pack > 0:
+ out(" packing_flags = ($(PACK_TYPES[pack])) dec.readUint%s();\n" % (pack*8));
+
+for f in fields:
+ if f.empty:
+ continue
+ if pack > 0:
+ out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ")
+ pre = ""
+ post = ""
+ arg = ""
+ if f.type_node.name == "struct":
+ pre = "(%s)" % cname(f.type_node)
+ arg = "%s.TYPE" % cname(f.type_node)
+ elif f.type_node.name == "domain":
+ pre = "%s.get(" % cname(f.type_node)
+ post = ")"
+ out(" this.$(f.name) = $(pre)dec.read$(f.coder)($(arg))$(post);\n")
+}
+ }
+
+ public Map<String,Object> getFields()
+ {
+ Map<String,Object> result = new LinkedHashMap<String,Object>();
+
+${
+for f in fields:
+ if pack > 0:
+ out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ")
+ out(' result.put("$(f.name)", $(f.get)());\n')
+}
+
+ return result;
+ }
+
+}
diff --git a/RC9/qpid/java/common/Constant.tpl b/RC9/qpid/java/common/Constant.tpl
new file mode 100644
index 0000000000..da4233c847
--- /dev/null
+++ b/RC9/qpid/java/common/Constant.tpl
@@ -0,0 +1,35 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+${from genutil import *}
+
+public interface Constant
+{
+${
+constants = spec.query["amqp/constant"]
+
+for c in constants:
+ name = scream(c["@name"])
+ value = c["@value"]
+ out(" public static final int $name = $value;\n")
+}}
diff --git a/RC9/qpid/java/common/Enum.tpl b/RC9/qpid/java/common/Enum.tpl
new file mode 100644
index 0000000000..0835d34a20
--- /dev/null
+++ b/RC9/qpid/java/common/Enum.tpl
@@ -0,0 +1,57 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+public enum $name {
+${
+from genutil import *
+
+vtype = jtype(resolve_type(type))
+
+choices = [(scream(ch["@name"]), "(%s) %s" % (vtype, ch["@value"]))
+ for ch in type.query["enum/choice"]]
+}
+ $(",\n ".join(["%s(%s)" % ch for ch in choices]));
+
+ private final $vtype value;
+
+ $name($vtype value)
+ {
+ this.value = value;
+ }
+
+ public $vtype getValue()
+ {
+ return value;
+ }
+
+ public static $name get($vtype value)
+ {
+ switch (value)
+ {
+${
+for ch, value in choices:
+ out(' case $value: return $ch;\n')
+} default: throw new IllegalArgumentException("no such value: " + value);
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/Invoker.tpl b/RC9/qpid/java/common/Invoker.tpl
new file mode 100644
index 0000000000..2eed43ad28
--- /dev/null
+++ b/RC9/qpid/java/common/Invoker.tpl
@@ -0,0 +1,73 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public abstract class $(invoker) {
+${
+from genutil import *
+
+results = False
+
+for c in composites:
+ name = cname(c)
+ fields = get_fields(c)
+ params = get_parameters(c, fields)
+ args = get_arguments(c, fields)
+ result = c["result"]
+ if result:
+ results = True
+ if not result["@type"]:
+ rname = cname(result["struct"])
+ else:
+ rname = cname(result, "@type")
+ jresult = "Future<%s>" % rname
+ jreturn = "return "
+ jclass = ", %s.class" % rname
+ else:
+ jresult = "void"
+ jreturn = ""
+ jclass = ""
+
+ if c.name == "command":
+ access = "public "
+ else:
+ access = ""
+
+ out("""
+ $(access)final $jresult $(dromedary(name))($(", ".join(params))) {
+ $(jreturn)invoke(new $name($(", ".join(args)))$jclass);
+ }
+""")
+}
+ protected abstract void invoke(Method method);
+${
+if results:
+ out("""
+ protected abstract <T> Future<T> invoke(Method method, Class<T> resultClass);
+""")
+}
+}
diff --git a/RC9/qpid/java/common/MethodDelegate.tpl b/RC9/qpid/java/common/MethodDelegate.tpl
new file mode 100644
index 0000000000..27e20a7ef2
--- /dev/null
+++ b/RC9/qpid/java/common/MethodDelegate.tpl
@@ -0,0 +1,37 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+public abstract class MethodDelegate<C> {
+
+ public abstract void handle(C context, Method method);
+${
+from genutil import *
+
+for c in composites:
+ name = cname(c)
+ out("""
+ public void $(dromedary(name))(C context, $name method) {
+ handle(context, method);
+ }""")
+}
+}
diff --git a/RC9/qpid/java/common/Option.tpl b/RC9/qpid/java/common/Option.tpl
new file mode 100644
index 0000000000..776b211ad5
--- /dev/null
+++ b/RC9/qpid/java/common/Option.tpl
@@ -0,0 +1,41 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+public enum Option {
+
+${
+from genutil import *
+
+options = {}
+
+for c in composites:
+ for f in c.query["field"]:
+ t = resolve_type(f)
+ if t["@name"] == "bit":
+ option = scream(f["@name"])
+ if not options.has_key(option):
+ options[option] = None
+ out(" $option,\n")}
+ BATCH,
+ NONE
+}
diff --git a/RC9/qpid/java/common/StructFactory.tpl b/RC9/qpid/java/common/StructFactory.tpl
new file mode 100644
index 0000000000..09c669f74e
--- /dev/null
+++ b/RC9/qpid/java/common/StructFactory.tpl
@@ -0,0 +1,60 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+class StructFactory {
+
+ public static Struct create(int type)
+ {
+ switch (type)
+ {
+${
+from genutil import *
+
+fragment = """ case $name.TYPE:
+ return new $name();
+"""
+
+for c in composites:
+ name = cname(c)
+ if c.name == "struct":
+ out(fragment)
+} default:
+ throw new IllegalArgumentException("type: " + type);
+ }
+ }
+
+ public static Struct createInstruction(int type)
+ {
+ switch (type)
+ {
+${
+for c in composites:
+ name = cname(c)
+ if c.name in ("command", "control"):
+ out(fragment)
+} default:
+ throw new IllegalArgumentException("type: " + type);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/Type.tpl b/RC9/qpid/java/common/Type.tpl
new file mode 100644
index 0000000000..7f9cfee268
--- /dev/null
+++ b/RC9/qpid/java/common/Type.tpl
@@ -0,0 +1,84 @@
+package org.apache.qpid.transport;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+${from genutil import *}
+
+public enum Type
+{
+
+${
+types = spec.query["amqp/type"] + spec.query["amqp/class/type"]
+codes = {}
+first = True
+for t in types:
+ code = t["@code"]
+ fix_width = t["@fixed-width"]
+ var_width = t["@variable-width"]
+
+ if code is None:
+ continue
+
+ if fix_width is None:
+ width = var_width
+ fixed = "false"
+ else:
+ width = fix_width
+ fixed = "true"
+
+ name = scream(t["@name"])
+ codes[code] = name
+
+ if first:
+ first = False
+ else:
+ out(",\n")
+
+ out(" $name((byte) $code, $width, $fixed)")
+};
+
+ public byte code;
+ public int width;
+ public boolean fixed;
+
+ Type(byte code, int width, boolean fixed)
+ {
+ this.code = code;
+ this.width = width;
+ this.fixed = fixed;
+ }
+
+ public static Type get(byte code)
+ {
+ switch (code)
+ {
+${
+keys = list(codes.keys())
+keys.sort()
+
+for code in keys:
+ out(" case (byte) $code: return $(codes[code]);\n")
+}
+ default: return null;
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/bin/qpid-run b/RC9/qpid/java/common/bin/qpid-run
new file mode 100755
index 0000000000..0b5070d937
--- /dev/null
+++ b/RC9/qpid/java/common/bin/qpid-run
@@ -0,0 +1,264 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Test if we're running on cygwin.
+cygwin=false
+if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then
+ cygwin=true
+fi
+
+die() {
+ if [[ $1 = -usage ]]; then
+ shift
+ usage=true
+ else
+ usage=false
+ fi
+ echo "$@"
+ $usage && echo
+ $usage && usage
+ exit 1
+}
+
+OFF=0
+WARN=1
+INFO=2
+
+if [ -z "$QPID_RUN_LOG" ]; then
+ QPID_RUN_LOG=$OFF
+fi
+
+log() {
+ if [ "$1" -le "$QPID_RUN_LOG" ]; then
+ shift
+ echo "$@"
+ fi
+}
+
+if [ -z $AMQJ_LOGGING_LEVEL ]; then
+ export AMQJ_LOGGING_LEVEL=info
+fi
+
+#Set to help us get round the manifold problems of ps/pgrep on various
+#platforms which gather up to prevent qpid_stop from working .....
+if [ -z "$QPID_PNAME" ]; then
+ export QPID_PNAME=" -DPNAME=QPBRKR"
+fi
+
+if [ -z "$QPID_HOME" ]; then
+ export QPID_HOME=$(dirname $(dirname $(readlink -f $0)))
+ export PATH=${PATH}:${QPID_HOME}/bin
+fi
+
+if [ -z "$QPID_WORK" ]; then
+ log $INFO Setting QPID_WORK to $HOME as default
+ QPID_WORK=$HOME
+fi
+
+if [ -z "$JAVA" ]; then
+ JAVA=java
+fi
+
+if $cygwin; then
+ QPID_HOME=$(cygpath -w $QPID_HOME)
+ QPID_WORK=$(cygpath -w $QPID_WORK)
+fi
+
+#Set the default system properties that we'll use now that they have
+#all been initialised
+SYSTEM_PROPS="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL -DQPID_HOME=$QPID_HOME -DQPID_WORK=$QPID_WORK"
+
+#If logprefix or logsuffix set to use PID make that happen
+#Otherwise just pass the value through for these props
+#Using X character to avoid probs with empty strings
+if [ -n "$QPID_LOG_PREFIX" ]; then
+ if [ "X$QPID_LOG_PREFIX" = "XPID" ]; then
+ log $INFO Using pid in qpid log name prefix
+ LOG_PREFIX=" -Dlogprefix=$$"
+ else
+ log $INFO Using qpid logprefix property
+ LOG_PREFIX=" -Dlogprefix=$QPID_LOG_PREFIX"
+ fi
+ SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_PREFIX}"
+fi
+
+if [ -n "$QPID_LOG_SUFFIX" ]; then
+ if [ "X$QPID_LOG_SUFFIX" = "XPID" ]; then
+ log $INFO Using pid in qpid log name suffix
+ LOG_SUFFIX=" -Dlogsuffix=$$"
+ else
+ log $INFO Using qpig logsuffix property
+ LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX"
+ fi
+ SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}"
+fi
+
+log $INFO System Properties set to $SYSTEM_PROPS
+
+program=$(basename $0)
+sourced=${BASH_SOURCE[0]}
+if [[ -z ${sourced:-''} ]]; then
+ sourced=$(which qpid-run) || ${QPID_HOME}/bin/qpid-run
+fi
+
+usage() {
+ echo Usage: $program ... "[-run:<option>]" ...
+ echo
+ echo Options:
+ egrep -B 1 "^\s*#USAGE: " ${sourced} |\
+ sed "s/#USAGE:/ /" |\
+ sed "s/-run:\(.*\))/-run:\1/" |\
+ sed "s/-run:\(.*\)=\*/-run:\1=<value>/" |\
+ sed "s/^--$//"
+}
+
+export EXTERNAL_CLASSPATH=$CLASSPATH
+unset CLASSPATH
+
+#Use QPID_CLASSPATH if set
+if [ -n "$QPID_CLASSPATH" ]; then
+ export CLASSPATH=$QPID_CLASSPATH
+ log $INFO "Using QPID_CLASSPATH" $QPID_CLASSPATH
+else
+ log $WARN "Warning: Qpid classpath not set. CLASSPATH must include qpid jars."
+fi
+
+#Use QPID_JAVA_GC if set
+if [ -n "$QPID_JAVA_GC" ]; then
+ export JAVA_GC=$QPID_JAVA_GC
+ log $INFO "Using QPID_JAVA_GC setting" $QPID_JAVA_GC
+else
+ log $INFO "Info: QPID_JAVA_GC not set. Defaulting to JAVA_GC" $JAVA_GC
+fi
+
+
+#Use QPID_JAVA_MEM if set
+if [ -n "$QPID_JAVA_MEM" ]; then
+ export JAVA_MEM=$QPID_JAVA_MEM
+ log $INFO "Using QPID_JAVA_MEM setting" $QPID_JAVA_MEM
+else
+ log $INFO "Info: QPID_JAVA_MEM not set. Defaulting to JAVA_MEM" $JAVA_MEM
+fi
+
+declare -a RUN_ARGS JAVA_ARGS
+for arg in "$@"; do
+ if [[ $arg == -run:* ]]; then
+ RUN_ARGS[${#RUN_ARGS[@]}]="$arg"
+ else
+ JAVA_ARGS[${#JAVA_ARGS[@]}]="$arg"
+ fi
+done
+
+# this defines the default behavior, it may be modified during option
+# processing below
+DISPATCH() {
+ if $debug; then
+ echo "CLASSPATH=${CLASSPATH}"
+ echo "${COMMAND[@]}"
+ fi
+
+ exec "${COMMAND[@]}"
+}
+
+exclusive() {
+ if [ -z "$PREVIOUS_ARGS" ]; then
+ PREVIOUS_ARGS=$1
+ else
+ PREVIOUS_ARGS="${PREVIOUS_ARGS}, $1"
+ DISPATCH() {
+ die -usage "you must choose one of: $PREVIOUS_ARGS"
+ }
+ fi
+}
+
+debug=false
+
+for arg in "${RUN_ARGS[@]}"; do
+ case $arg in
+ -run:debug)
+#USAGE: print the classpath and command before running it
+ debug=true
+ ;;
+ -run:jpda)
+#USAGE: adds debugging options to the java command, use
+#USAGE: JPDA_TRANSPORT and JPDA_ADDRESS to customize the debugging
+#USAGE: behavior and use JPDA_OPTS to override it entirely
+ if [ -z "$JPDA_OPTS" ]; then
+ JPDA_OPTS="-Xdebug -Xrunjdwp:transport=${JPDA_TRANSPORT:-dt_socket},address=${JPDA_ADDRESS:-8000},server=y,suspend=n"
+ fi
+ QPID_OPTS="${QPID_OPTS} ${JPDA_OPTS}"
+ ;;
+ -run:external-classpath=*)
+#USAGE: controls how the CLASSPATH environment variable is used by
+#USAGE: this script, value can be one of ignore (the default), first,
+#USAGE: last, and only
+ case $arg in
+ *=ignore)
+ # do nothing
+ ;;
+ *=first)
+ CLASSPATH=$EXTERNAL_CLASSPATH:$CLASSPATH
+ ;;
+ *=last)
+ CLASSPATH=$CLASSPATH:$EXTERNAL_CLASSPATH
+ ;;
+ *=only)
+ CLASSPATH=$EXTERNAL_CLASSPATH
+ ;;
+ *)
+ die -usage $(echo $arg | sed "s/=/: invalid value '/")\'
+ ;;
+ esac
+ ;;
+ -run:print-classpath)
+#USAGE: print the classpath
+ DISPATCH() {
+ echo $CLASSPATH
+ }
+ exclusive $arg
+ ;;
+ -run:print-command)
+#USAGE: print the command
+ DISPATCH() {
+ echo "${COMMAND[@]}"
+ }
+ exclusive $arg
+ ;;
+ -run:help)
+#USAGE: print this message
+ DISPATCH() {
+ usage
+ }
+ exclusive $arg
+ ;;
+ *)
+ die -usage "unrecognized -run option '$arg'"
+ ;;
+ esac
+done
+
+if $cygwin; then
+ CLASSPATH=$(cygpath -w -p $CLASSPATH)
+ JAVA=$(cygpath -u $JAVA)
+fi
+
+COMMAND=($JAVA $JAVA_VM $QPID_PNAME $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}")
+
+DISPATCH
diff --git a/RC9/qpid/java/common/build.xml b/RC9/qpid/java/common/build.xml
new file mode 100644
index 0000000000..dfe060cdef
--- /dev/null
+++ b/RC9/qpid/java/common/build.xml
@@ -0,0 +1,104 @@
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<project name="AMQ Common" default="build">
+
+ <import file="../module.xml"/>
+
+ <property name="gentools.home" location="${project.root}/../gentools" />
+ <property name="generated.package" value="org/apache/qpid/framing" />
+ <property name="generated.dir" location="${module.precompiled}/${generated.package}" />
+ <property name="xml.spec.dir" location="${project.root}/../specs" />
+ <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml" />
+ <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" />
+ <property name="gentools.timestamp" location="${generated.dir}/gentools.timestamp" />
+ <property name="jython.timestamp" location="${generated.dir}/jython.timestamp" />
+
+ <target name="check_jython_deps">
+ <uptodate property="jython.notRequired" targetfile="${jython.timestamp}">
+ <srcfiles dir="${xml.spec.dir}" includes="amqp.0-10-qpid-errata.xml" />
+ </uptodate>
+ </target>
+
+ <target name="jython" depends="check_jython_deps" unless="jython.notRequired">
+ <jython path="${mllib.dir}">
+ <args>
+ <arg value="${basedir}/codegen"/>
+ <arg value="${module.precompiled}"/>
+ <arg value="${xml.spec.dir}/amqp.0-10-qpid-errata.xml"/>
+ <arg value="${basedir}"/>
+ </args>
+ </jython>
+ <touch file="${jython.timestamp}" />
+ </target>
+
+ <target name="compile_gentools">
+ <ant dir="${gentools.home}" />
+ </target>
+
+ <target name="check_gentool_deps">
+ <uptodate property="gentools.notRequired" targetfile="${gentools.timestamp}">
+ <srcfiles dir="${xml.spec.dir}" includes="${xml.spec.deps}" />
+ </uptodate>
+ </target>
+
+ <target name="gentools" depends="compile_gentools,check_gentool_deps" unless="gentools.notRequired">
+ <mkdir dir="${generated.dir}"/>
+ <java classname="org.apache.qpid.gentools.Main" fork="true" dir="${gentools.home}/src" failonerror="true">
+ <arg line="-j -o ${generated.dir} -t ${project.root}/common/templates ${xml.spec.list}"/>
+ <classpath>
+ <pathelement path="${gentools.home}/src" />
+ <fileset dir="${gentools.home}/lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${gentools.home}/lib/velocity-1.4.jar" />
+ <pathelement path="${gentools.home}/lib/velocity-dep-1.4.jar" />
+ </classpath>
+ </java>
+ <touch file="${gentools.timestamp}" />
+ </target>
+
+ <property name="version.file" location="${module.classes}/qpidversion.properties"/>
+ <property file="${version.file}" prefix="old."/>
+
+ <target name="check-version">
+ <exec executable="svnversion" spawn="false" failifexecutionfails="false"
+ dir="${project.root}" outputproperty="qpid.svnversion">
+ <arg line="."/>
+ </exec>
+ <condition property="version.stale">
+ <not>
+ <equals arg1="${qpid.svnversion}" arg2="${old.qpid.svnversion}"/>
+ </not>
+ </condition>
+ </target>
+
+ <target name="version" depends="check-version" if="version.stale">
+ <!-- Write the version.properties out. -->
+ <echo file="${version.file}" append="true">
+ qpid.svnversion=${qpid.svnversion}
+ qpid.name=${project.name}
+ qpid.version=${project.version}
+ </echo>
+ </target>
+
+ <target name="precompile" depends="gentools,jython,version"/>
+
+</project>
diff --git a/RC9/qpid/java/common/codegen b/RC9/qpid/java/common/codegen
new file mode 100755
index 0000000000..6a1effc07b
--- /dev/null
+++ b/RC9/qpid/java/common/codegen
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 os, sys, mllib
+from templating import Parser
+from genutil import *
+
+out_dir = sys.argv[1]
+spec_file = sys.argv[2]
+tpl_dir = sys.argv[3]
+pkg_dir = os.path.join(out_dir, "org/apache/qpid/transport")
+
+if not os.path.exists(pkg_dir):
+ os.makedirs(pkg_dir)
+
+spec = mllib.xml_parse(spec_file)
+
+def excludes(nd):
+ if (nd.parent is not None and
+ nd.parent.name == "class" and
+ nd.parent["@name"] in ("file", "stream")):
+ return False
+ else:
+ return True
+
+def execute(output, template, **kwargs):
+ f = open(os.path.join(tpl_dir, template))
+ input = f.read()
+ f.close()
+ p = Parser(**kwargs)
+ p.parse(input)
+ fname = os.path.join(pkg_dir, output)
+ f = open(fname, "w")
+ f.write(p.output)
+ f.close()
+
+execute("Type.java", "Type.tpl", spec = spec)
+execute("Constant.java", "Constant.tpl", spec = spec)
+
+structs = spec.query["amqp/struct"] + \
+ spec.query["amqp/class/struct", excludes] + \
+ spec.query["amqp/class/command/result/struct", excludes]
+controls = spec.query["amqp/class/control", excludes]
+commands = spec.query["amqp/class/command", excludes]
+
+composites = structs + controls + commands
+actions = controls + commands
+connection = [c for c in actions if c.parent["@name"] == "connection"]
+session = [c for c in actions if c.parent["@name"] != "connection"]
+
+for c in composites:
+ name = cname(c)
+ execute("%s.java" % name, "Composite.tpl", type = c, name = name)
+
+execute("MethodDelegate.java", "MethodDelegate.tpl", composites = actions)
+execute("Option.java", "Option.tpl", composites = composites)
+execute("ConnectionInvoker.java", "Invoker.tpl", invoker = "ConnectionInvoker",
+ composites = connection)
+execute("SessionInvoker.java", "Invoker.tpl", invoker = "SessionInvoker",
+ composites = session)
+execute("StructFactory.java", "StructFactory.tpl", composites = composites)
+
+def is_enum(nd):
+ return nd["enum"] is not None
+
+enums = spec.query["amqp/domain", is_enum] + \
+ spec.query["amqp/class/domain", is_enum]
+
+for e in enums:
+ name = cname(e)
+ execute("%s.java" % name, "Enum.tpl", name = name, type = e)
diff --git a/RC9/qpid/java/common/etc/qpid-run.conf b/RC9/qpid/java/common/etc/qpid-run.conf
new file mode 100644
index 0000000000..b9765fe3ce
--- /dev/null
+++ b/RC9/qpid/java/common/etc/qpid-run.conf
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+QPID_LIBS=$(find ${QPID_HOME}/lib -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/RC9/qpid/java/common/etc/qpid-run.conf.dev b/RC9/qpid/java/common/etc/qpid-run.conf.dev
new file mode 100644
index 0000000000..a5419eb4e8
--- /dev/null
+++ b/RC9/qpid/java/common/etc/qpid-run.conf.dev
@@ -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.
+#
+
+QPID_LIBS=$(find ${QPID_HOME} -type d -name "classes" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+QPID_LIBS=${QPID_LIBS}:$(find $(dirname ${QPID_HOME}) -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/RC9/qpid/java/common/genutil.py b/RC9/qpid/java/common/genutil.py
new file mode 100644
index 0000000000..5a75c2e48c
--- /dev/null
+++ b/RC9/qpid/java/common/genutil.py
@@ -0,0 +1,255 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+def camel(offset, *args):
+ parts = []
+ for a in args:
+ parts.extend(a.split("-"))
+ return "".join(parts[:offset] + [p[0].upper() + p[1:] for p in parts[offset:]])
+
+def dromedary(s):
+ return s[0].lower() + s[1:]
+
+def scream(*args):
+ return "_".join([a.replace("-", "_").upper() for a in args])
+
+def num(x, default=None):
+ if x is not None and x != "":
+ return int(x, 0)
+ else:
+ return default
+
+def klass(nd):
+ parent = nd.parent
+ while parent is not None:
+ if hasattr(parent, "name") and parent.name == "class":
+ return parent
+ parent = parent.parent
+
+untyped = -1
+
+def code(nd):
+ global untyped
+ cd = num(nd["@code"])
+ if cd is None:
+ cd = untyped
+ untyped -= 1
+ return cd
+
+ cls = klass(nd)
+ if cls:
+ cd |= (num(cls["@code"]) << 8)
+ return cd
+
+def root(nd):
+ if nd.parent is None:
+ return nd
+ else:
+ return root(nd.parent)
+
+def qname(nd):
+ name = nd["@name"]
+ cls = klass(nd)
+ if cls != None:
+ return "%s.%s" % (cls["@name"], name)
+ else:
+ return name
+
+RESOLVED = {}
+
+def resolve(node, name):
+ key = (node, name)
+ if RESOLVED.has_key(key):
+ return RESOLVED[key]
+ else:
+ spec = root(node)
+ cls = klass(node)
+ if cls:
+ for nd in cls.query["#tag"]:
+ if nd["@name"] == name:
+ RESOLVED[key] = nd
+ return nd
+ for nd in spec.query["amqp/#tag"] + spec.query["amqp/class/#tag"]:
+ if name == qname(nd):
+ RESOLVED[key] = nd
+ return nd
+ raise Exception("unresolved name: %s" % name)
+
+def resolve_type(nd):
+ if hasattr(nd, "_resolved_type"):
+ return nd._resolved_type
+ else:
+ name = nd["@type"]
+ type = resolve(nd, name)
+ if type.name == "domain" and not type["enum"]:
+ type = resolve_type(type)
+ nd._resolved_type = type
+ return type
+
+TYPES = {
+ "bit": "boolean",
+ "uint8": "short",
+ "uint16": "int",
+ "uint32": "long",
+ "uint64": "long",
+ "datetime": "long",
+ "uuid": "UUID",
+ "sequence-no": "int",
+ "sequence-set": "RangeSet", # XXX
+ "byte-ranges": "RangeSet", # XXX
+ "str8": "String",
+ "str16": "String",
+ "vbin8": "byte[]",
+ "vbin16": "byte[]",
+ "vbin32": "byte[]",
+ "struct32": "Struct",
+ "map": "Map<String,Object>",
+ "array": "List<Object>"
+ }
+
+def cname(nd, field="@name"):
+ cls = klass(nd)
+ if cls:
+ if (nd.name in ("struct", "result") and
+ cls["@name"] != "session" and
+ nd[field] != "header"):
+ return camel(0, nd[field])
+ else:
+ return camel(0, cls["@name"], nd[field])
+ else:
+ return camel(0, nd[field])
+
+def jtype(nd):
+ if nd.name == "struct" or nd["enum"]:
+ return cname(nd)
+ else:
+ return TYPES[nd["@name"]]
+
+REFS = {
+ "boolean": "Boolean",
+ "byte": "Byte",
+ "short": "Short",
+ "int": "Integer",
+ "long": "Long",
+ "float": "Float",
+ "double": "Double",
+ "char": "Character"
+}
+
+def jref(jt):
+ return REFS.get(jt, jt)
+
+def jclass(jt):
+ idx = jt.find('<')
+ if idx > 0:
+ return jt[:idx]
+ else:
+ return jt
+
+DEFAULTS = {
+ "long": 0,
+ "int": 0,
+ "short": 0,
+ "byte": 0,
+ "char": 0,
+ "boolean": "false"
+ }
+
+class Field:
+
+ def __init__(self, index, nd):
+ self.index = index
+ self.name = camel(1, nd["@name"])
+ self.type_node = resolve_type(nd)
+ if self.type_node.name == "domain":
+ self.prim_type = resolve_type(self.type_node)
+ else:
+ self.prim_type = self.type_node
+ self.variable_width = num(self.prim_type["@variable-width"], 0)
+ self.fixed_width = num(self.prim_type["@fixed-width"], 0)
+ self.empty = self.variable_width == 0 and self.fixed_width == 0 and self.prim_type.name != "struct"
+ tname = cname(self.type_node)
+ if self.type_node.name == "struct":
+ self.read = "(%s) dec.readStruct(%s.TYPE)" % (tname, tname)
+ self.write = "enc.writeStruct(%s.TYPE, check(struct).%s)" % (tname, self.name)
+ self.coder = "Struct"
+ elif self.type_node.name == "domain":
+ self.coder = camel(0, self.prim_type["@name"])
+ self.read = "%s.get(dec.read%s())" % (tname, self.coder)
+ self.write = "enc.write%s(check(struct).%s.getValue())" % (self.coder, self.name)
+ else:
+ self.coder = camel(0, self.type_node["@name"])
+ self.read = "dec.read%s()" % self.coder
+ self.write = "enc.write%s(check(struct).%s)" % (self.coder, self.name)
+ self.type = jtype(self.type_node)
+ self.default = DEFAULTS.get(self.type, "null")
+ self.has = camel(1, "has", self.name)
+ self.get = camel(1, "get", self.name)
+ self.set = camel(1, "set", self.name)
+ self.clear = camel(1, "clear", self.name)
+ if self.type == "boolean":
+ self.option = scream(nd["@name"])
+ else:
+ self.option = None
+
+ def flag_mask(self, pack):
+ flag = pack * 8 - 8 - (self.index/8)*8 + (self.index % 8)
+ return 1 << flag
+
+
+def get_fields(nd):
+ fields = []
+ index = 0
+ for f in nd.query["field"]:
+ fields.append(Field(index, f))
+ index += 1
+ return fields
+
+def get_parameters(type, fields):
+ params = []
+ options = False
+ for f in fields:
+ if f.option:
+ options = True
+ else:
+ params.append("%s %s" % (f.type, f.name))
+ if type["segments"]:
+ params.append("Header header")
+ params.append("ByteBuffer body")
+ if options or type.name in ("control", "command"):
+ params.append("Option ... _options")
+ return params
+
+def get_arguments(type, fields):
+ args = []
+ options = False
+ for f in fields:
+ if f.option:
+ options = True
+ else:
+ args.append(f.name)
+ if type["segments"]:
+ args.append("header")
+ args.append("body")
+ if options or type.name in ("control", "command"):
+ args.append("_options")
+ return args
+
+def get_options(fields):
+ return [f for f in fields if f.option]
diff --git a/RC9/qpid/java/common/protocol-version.xml b/RC9/qpid/java/common/protocol-version.xml
new file mode 100644
index 0000000000..ee9db6049b
--- /dev/null
+++ b/RC9/qpid/java/common/protocol-version.xml
@@ -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.
+ -
+ -->
+<project name="Qpid Common Protocol Versions" default="generate">
+ <property name="topDirectoryLocation" location=".." />
+ <property name="project.build.directory" location="target" />
+ <property name="gentools.home" location="${topDirectoryLocation}/../gentools" />
+ <property name="generated.path" location="${project.build.directory}/generated-sources/gentools" />
+ <property name="generated.package" value="org/apache/qpid/framing" />
+ <property name="generated.dir" location="${generated.path}/${generated.package}" />
+ <property name="generated.timestamp" location="${generated.dir}/timestamp" />
+ <property name="xml.spec.dir" location="${topDirectoryLocation}/../specs" />
+ <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml" />
+ <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" />
+ <property name="template.dir" value="${topDirectoryLocation}/common/templates" />
+
+
+ <!--<target name="generate" depends="compile_generator,check_generate_deps" unless="generation.notRequired">-->
+ <target name="generate" depends="compile_generator" unless="generation.notRequired">
+ <mkdir dir="${generated.dir}"/>
+ <java classname="org.apache.qpid.gentools.Main" fork="true" dir="${gentools.home}/src" failonerror="true">
+ <arg line="-j -o ${generated.dir} -t ${template.dir} ${xml.spec.list}" />
+ <classpath>
+ <pathelement path="${gentools.home}/src" />
+ <fileset dir="${gentools.home}/lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${gentools.home}/lib/velocity-1.4.jar" />
+ <pathelement path="${gentools.home}/lib/velocity-dep-1.4.jar" />
+ </classpath>
+ </java>
+ <touch file="${generated.timestamp}" />
+ </target>
+
+ <target name="check_generate_deps">
+ <uptodate property="generation.notRequired" targetfile="${generated.timestamp}">
+ <srcfiles dir="${xml.spec.dir}" includes="${xml.spec.deps}" />
+ <srcfiles dir="${template.dir}" includes="**/*.vm **/*.tmpl" />
+ </uptodate>
+ </target>
+
+ <target name="compile_generator">
+ <ant dir="${gentools.home}" />
+ </target>
+
+ <target name="precompile" depends="generate"/>
+
+ <target name="clean">
+ <delete dir="${generated.path}" />
+ </target>
+
+</project>
+
diff --git a/RC9/qpid/java/common/readme.txt b/RC9/qpid/java/common/readme.txt
new file mode 100644
index 0000000000..12841fa08d
--- /dev/null
+++ b/RC9/qpid/java/common/readme.txt
@@ -0,0 +1,4 @@
+AMQP Common Java API
+
+Common generated functionality for AMQP Java client and broker. See the
+readme in the client and broker directories.
diff --git a/RC9/qpid/java/common/src/main/java/common.bnd b/RC9/qpid/java/common/src/main/java/common.bnd
new file mode 100755
index 0000000000..a534451390
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/log4j.properties b/RC9/qpid/java/common/src/main/java/log4j.properties
new file mode 100644
index 0000000000..44f89dc805
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
new file mode 100644
index 0000000000..0c311b6645
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java
new file mode 100644
index 0000000000..4fd28c4eb5
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java
new file mode 100644
index 0000000000..5723ffbaa9
--- /dev/null
+++ b/RC9/qpid/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 <tt>serviceAddress</tt>es.
+ */
+ private final Set managedServiceAddresses = new HashSet();
+
+ /**
+ * Tracks managed sesssions with <tt>serviceAddress</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java
new file mode 100644
index 0000000000..47f19aa76d
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java
new file mode 100644
index 0000000000..4e9db9071a
--- /dev/null
+++ b/RC9/qpid/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
+ *
+ * <p/>
+ * <p>Usage:
+ * <p/>
+ * <pre><code>
+ * DefaultFilterChainBuilder builder = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( builder );
+ * </code></pre>
+ * <p/>
+ * or
+ * <p/>
+ * <pre><code>
+ * IoFilterChain chain = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( chain );
+ * </code></pre>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
new file mode 100644
index 0000000000..3f7e206cb4
--- /dev/null
+++ b/RC9/qpid/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 <em>cumulative buffer</em> to help users implement decoders.
+ * <p>
+ * 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.
+ * <p>
+ * Here is an example decoder that decodes CRLF terminated lines into
+ * <code>Command</code> objects:
+ * <pre>
+ * 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;
+ * }
+ * }
+ * </pre>
+ *
+ * @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 <tt>in</tt> into internal buffer and forwards
+ * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}.
+ * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt>
+ * and the cumulative buffer is NOT compacted after decoding ends.
+ *
+ * @throws IllegalStateException if your <tt>doDecode()</tt> returned
+ * <tt>true</tt> 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 <tt>true</tt> if and only if there's more to decode in the buffer
+ * and you want to have <tt>doDecode</tt> method invoked again.
+ * Return <tt>false</tt> 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 <tt>in</tt>.
+ */
+ protected abstract boolean doDecode(IoSession session, ByteBuffer in,
+ ProtocolDecoderOutput out) throws Exception;
+
+ /**
+ * Releases the cumulative buffer used by the specified <tt>session</tt>.
+ * Please don't forget to call <tt>super.dispose( session )</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java
new file mode 100644
index 0000000000..b8c6f29720
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java
new file mode 100644
index 0000000000..e5360d32e0
--- /dev/null
+++ b/RC9/qpid/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 <code>address</code> and handles incoming connections with the specified
+ * <code>handler</code>. Backlog value is configured to the value of <code>backlog</code> 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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
new file mode 100644
index 0000000000..b1612840db
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java
new file mode 100644
index 0000000000..67b8c8d820
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
new file mode 100644
index 0000000000..c23ad8686f
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java
new file mode 100644
index 0000000000..043d4800b6
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java
new file mode 100644
index 0000000000..be4a2d289d
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java b/RC9/qpid/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java
new file mode 100644
index 0000000000..a23e546af5
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java
new file mode 100644
index 0000000000..1b2eabdc86
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a failed operation on a closed channel.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelException.java
new file mode 100644
index 0000000000..ef9420ba87
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error that rquires the channel to be closed.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java
new file mode 100644
index 0000000000..b2ce3c1b32
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a the closure of a connection.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java
new file mode 100644
index 0000000000..afd415b1eb
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error that rquires the channel to be closed.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
new file mode 100644
index 0000000000..fa69f7f91b
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to connect to a broker.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQConnectionFailureException extends AMQException
+{
+ Collection<Exception> _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<Exception> 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<Exception> getLinkedExceptions()
+ {
+ return _exceptions;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java
new file mode 100644
index 0000000000..5ec5c42ab9
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents disconnection without failover by the broker.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQException.java
new file mode 100644
index 0000000000..be335c5dba
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an exception condition associated with an AMQ protocol status code.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
new file mode 100644
index 0000000000..baca2a4773
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error due to an invalid argument being passed to an AMQP method.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java
new file mode 100644
index 0000000000..c117968a29
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a format error in a routing key.
+ * </table>
+ */
+public class AMQInvalidRoutingKeyException extends AMQException
+{
+ public AMQInvalidRoutingKeyException(String message, Throwable cause)
+ {
+ super(AMQConstant.INVALID_ROUTING_KEY, message, cause);
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java
new file mode 100644
index 0000000000..a0574efa72
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents illegal argument type for field table values.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java
new file mode 100644
index 0000000000..bbc569839a
--- /dev/null
+++ b/RC9/qpid/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
+ * <p> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java
new file mode 100644
index 0000000000..4ae8282af5
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Indicates that an expected response from a broker took too long.
+ * </table>
+ */
+public class AMQTimeoutException extends AMQException
+{
+ public AMQTimeoutException(String message, Throwable cause)
+ {
+ super(AMQConstant.REQUEST_TIMEOUT, message, cause);
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
new file mode 100644
index 0000000000..01a569b693
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to delivery a message that must be delivered.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java
new file mode 100644
index 0000000000..0eefc03016
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents unknown exchange type request.
+ * <tr><td>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java
new file mode 100644
index 0000000000..eee3e6afcf
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failre to resolve a socket address.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetails.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetails.java
new file mode 100644
index 0000000000..63f67a7857
--- /dev/null
+++ b/RC9/qpid/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<String,String> getProperties();
+
+ /**
+ * Sets the properties associated with this connection
+ *
+ * @param props the new p[roperties.
+ */
+ public void setProperties(Map<String,String> props);
+
+ public void setProperty(String key,String value);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java
new file mode 100644
index 0000000000..201d43e21f
--- /dev/null
+++ b/RC9/qpid/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<String, String> _props = new HashMap<String, String>();
+ ;
+
+ //--- 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<String, String> 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<String, String> getProperties()
+ {
+ return _props;
+ }
+
+ /**
+ * Sets the properties associated with this connection
+ *
+ * @param props
+ */
+ public void setProperties(Map<String, String> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java
new file mode 100644
index 0000000000..f17782ebf4
--- /dev/null
+++ b/RC9/qpid/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<ByteBuffer>
+{
+
+ public void send(ByteBuffer buf)
+ {
+ System.out.println(str(buf));
+ }
+
+ public void flush()
+ {
+ // pass
+ }
+
+ public void close()
+ {
+ System.out.println("CLOSED");
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/ErrorCode.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ErrorCode.java
new file mode 100644
index 0000000000..0549869e71
--- /dev/null
+++ b/RC9/qpid/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;
+ }
+ }
+ }
+
+/*
+
+<constant name="internal-error" value="541" class="hard-error">
+<doc>
+ 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.
+</doc>
+</constant>
+
+<constant name="invalid-argument" value="542" class="hard-error">
+<doc>
+ An invalid or illegal argument was passed to a method, and the operation could not proceed.
+</doc>
+</constant>
+*/
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidConfig.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidConfig.java
new file mode 100644
index 0000000000..9c8019f109
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/QpidException.java
new file mode 100644
index 0000000000..8503adaef8
--- /dev/null
+++ b/RC9/qpid/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.
+ * <p> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/SecurityHelper.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/SecurityHelper.java
new file mode 100644
index 0000000000..dda5a6506d
--- /dev/null
+++ b/RC9/qpid/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<Object> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/SerialException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/SerialException.java
new file mode 100644
index 0000000000..c59a6af779
--- /dev/null
+++ b/RC9/qpid/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 <code>SerialException</code> with the specified
+ * detail message.
+ *
+ * @param message The exception message.
+ */
+ public SerialException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyBroker.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyBroker.java
new file mode 100644
index 0000000000..db84b83adb
--- /dev/null
+++ b/RC9/qpid/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<String,Consumer> consumers = new ConcurrentHashMap<String,Consumer>();
+
+ 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<MessageTransfer> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyClient.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyClient.java
new file mode 100644
index 0000000000..3491af8cd2
--- /dev/null
+++ b/RC9/qpid/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<String,Object> nested = new LinkedHashMap<String,Object>();
+ nested.put("list", Arrays.asList("one", "two", "three"));
+ Map<String,Object> map = new LinkedHashMap<String,Object>();
+
+ 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<QueueQueryResult> future = ssn.queueQuery("asdf");
+ System.out.println(future.get().getQueue());
+ ssn.sync();
+ ssn.close();
+ conn.close();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyExchange.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ToyExchange.java
new file mode 100644
index 0000000000..da6aed9629
--- /dev/null
+++ b/RC9/qpid/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<String,List<LinkedBlockingQueue<MessageTransfer>>> directEx = new HashMap<String,List<LinkedBlockingQueue<MessageTransfer>>>();
+ private Map<String,List<LinkedBlockingQueue<MessageTransfer>>> topicEx = new HashMap<String,List<LinkedBlockingQueue<MessageTransfer>>>();
+ private Map<String,LinkedBlockingQueue<MessageTransfer>> queues = new HashMap<String,LinkedBlockingQueue<MessageTransfer>>();
+
+ public void createQueue(String name)
+ {
+ queues.put(name, new LinkedBlockingQueue<MessageTransfer>());
+ }
+
+ public LinkedBlockingQueue<MessageTransfer> getQueue(String name)
+ {
+ return queues.get(name);
+ }
+
+ public void bindQueue(String type,String binding,String queueName)
+ {
+ LinkedBlockingQueue<MessageTransfer> queue = queues.get(queueName);
+ binding = normalizeKey(binding);
+ if(DIRECT.equals(type))
+ {
+
+ if (directEx.containsKey(binding))
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> list = directEx.get(binding);
+ list.add(queue);
+ }
+ else
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> list = new LinkedList<LinkedBlockingQueue<MessageTransfer>>();
+ list.add(queue);
+ directEx.put(binding,list);
+ }
+ }
+ else
+ {
+ if (topicEx.containsKey(binding))
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> list = topicEx.get(binding);
+ list.add(queue);
+ }
+ else
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> list = new LinkedList<LinkedBlockingQueue<MessageTransfer>>();
+ list.add(queue);
+ topicEx.put(binding,list);
+ }
+ }
+ }
+
+ public boolean route(String dest, String routingKey, MessageTransfer msg)
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> 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<LinkedBlockingQueue<MessageTransfer>> matchWildCard(String routingKey)
+ {
+ List<LinkedBlockingQueue<MessageTransfer>> selected = new ArrayList<LinkedBlockingQueue<MessageTransfer>>();
+
+ for(String key: topicEx.keySet())
+ {
+ Pattern p = Pattern.compile(key);
+ Matcher m = p.matcher(routingKey);
+ if (m.find())
+ {
+ for(LinkedBlockingQueue<MessageTransfer> queue : topicEx.get(key))
+ {
+ selected.add(queue);
+ }
+ }
+ }
+
+ return selected;
+ }
+
+ private void storeMessage(MessageTransfer msg,List<LinkedBlockingQueue<MessageTransfer>> selected)
+ {
+ for(LinkedBlockingQueue<MessageTransfer> queue : selected)
+ {
+ queue.offer(msg);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/api/Message.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/api/Message.java
new file mode 100644
index 0000000000..df6f279026
--- /dev/null
+++ b/RC9/qpid/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
+ * <ul>
+ * <li> Memory (Ex: ByteBuffer)
+ * <li> To Disk
+ * <li> To Socket (Stream)
+ * </ul>
+ * @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
+ * <ul>
+ * <li> Memory (Ex: ByteBuffer)
+ * <li> To Disk
+ * <li> To Socket (Stream)
+ * </ul>
+ * @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
+ * <ul>
+ * <li> From memory (Ex: ByteBuffer)
+ * <li> From Disk
+ * <li> From Socket as and when it gets streamed
+ * </ul>
+ * @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
+ * <ul>
+ * <li> From memory (Ex: ByteBuffer)
+ * <li> From Disk
+ * <li> From Socket as and when it gets streamed
+ * </ul>
+ *
+ * @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
+ * <ul>
+ * <li>For message acquiring - If the transfer happend in no-acquire mode
+ * you could use this id to accquire it.
+ * <li>For releasing a message. You can use this id to release an acquired
+ * message
+ * <li>For Acknowledging a message - You need to pass this ID, in order to
+ * acknowledge the message
+ * <li>For Rejecting a message - You need to pass this ID, in order to reject
+ * the message.
+ * </ul>
+ *
+ * @return the message transfer id.
+ */
+ public int getMessageTransferId();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java
new file mode 100644
index 0000000000..fa890d0ebb
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations.
+ * <tr><td> Supply the protocol encoder. <td> {@link AMQEncoder}
+ * <tr><td> Supply the protocol decoder. <td> {@link AMQDecoder}
+ * </table>
+ */
+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 <tt>true</tt> if the first frame received is going to be a protocol initiation
+ * frame, <tt>false</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
new file mode 100644
index 0000000000..7eef73f337
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Delegate protocol initiation to its decoder. <td> {@link ProtocolInitiation.Decoder}
+ * <tr><td> Delegate AMQP data to its decoder. <td> {@link AMQDataBlockDecoder}
+ * <tr><td> Accept notification that protocol initiation has completed.
+ * </table>
+ *
+ * @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 <tt>true</tt> 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 <tt>true</tt> if the data was decoded, <tt>false<tt> 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 <tt>true</tt> if the data was decoded, <tt>false<tt> 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 <tt>true</tt> if the data was decoded, <tt>false<tt> 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 <tt>false</tt> once protocol initation completes.
+ *
+ * @param expectProtocolInitiation <tt>true</tt> to use the protocol initiation decoder, <tt>false</tt> to use the
+ * data decoder.
+ */
+ public void setExpectProtocolInitiation(boolean expectProtocolInitiation)
+ {
+ _expectProtocolInitiation = expectProtocolInitiation;
+ }
+
+
+ /**
+ * Cumulates content of <tt>in</tt> into internal buffer and forwards
+ * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}.
+ * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt>
+ * and the cumulative buffer is compacted after decoding ends.
+ *
+ * @throws IllegalStateException if your <tt>doDecode()</tt> returned
+ * <tt>true</tt> 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 <tt>session</tt>.
+ * Please don't forget to call <tt>super.dispose( session )</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java
new file mode 100644
index 0000000000..53f48ae1c8
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Delegate AMQP encoding. <td> {@link AMQDataBlockEncoder}
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java
new file mode 100644
index 0000000000..9ed915cc35
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent different consumer filter types.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
new file mode 100644
index 0000000000..7371c12519
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Specify the available client property types.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java
new file mode 100644
index 0000000000..2c783aeaa4
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><td>Load build versioning information into the runtime, for code identification purposes.
+ * </table>
+ *
+ * @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<Object, Object> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/Configured.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/Configured.java
new file mode 100644
index 0000000000..22903888fe
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java
new file mode 100644
index 0000000000..73a336321c
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaboration
+ * <tr><td> Represent failure to expand a property name into a value.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java
new file mode 100644
index 0000000000..6e2b25fb2c
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Expand system properties into strings with named expansions.
+ * </table>
+ *
+ * @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 <code>${xxx}</code>, 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 <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ *
+ * @return The original string with the properties replaced, or <code>null</code> if the original string is
+ * <code>null</code>.
+ *
+ * @throws PropertyException If the string contains an opening <code>${</code> without a balancing <code>}</code>,
+ * 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<String> fragments = new ArrayList<String>();
+ ArrayList<String> propertyRefs = new ArrayList<String>();
+ 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<String> fragments, ArrayList<String> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java
new file mode 100644
index 0000000000..49effc2dae
--- /dev/null
+++ b/RC9/qpid/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.
+ * <p> 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
+ * <p/>
+ * gtrid_length: how many bytes of this form the transaction id
+ * <p/>
+ * bqual_length: how many bytes of this form the branch id
+ * <p/>
+ * data: a sequence of octets of at most 128 bytes containing the txn id and the
+ * branch id
+ * <p/>
+ * 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.
+ * <p> Two Xids are equal if and only if their three elementary parts are equal
+ *
+ * @param o the object to compare this <code>XidImpl</code> against.
+ * @return true if the <code>XidImpl</code> 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
+ * <p/>
+ * gtrid_length: how many bytes of this form the transaction id
+ * <p/>
+ * bqual_length: how many bytes of this form the branch id
+ * <p/>
+ * data: a sequence of octets of at most 128 bytes containing the txn id and the
+ * branch id
+ * <p/>
+ * 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java
new file mode 100644
index 0000000000..123901b577
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Defines the standard AMQP exchange names.
+ * <tr><td> Defines the standard AMQP exchange types.
+ * </table>
+ *
+ * @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("<<default>>");
+
+ /** 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
new file mode 100644
index 0000000000..fe04155bb8
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java
new file mode 100644
index 0000000000..a2fc3a03ef
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java
new file mode 100644
index 0000000000..82ffc60802
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java
new file mode 100644
index 0000000000..05fd2bb480
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
new file mode 100644
index 0000000000..02a46f3748
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
new file mode 100644
index 0000000000..2373edb478
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a format error in a protocol frame.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java
new file mode 100644
index 0000000000..4763b22290
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
new file mode 100644
index 0000000000..1a7022c11b
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java
new file mode 100644
index 0000000000..ad7f36f790
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java
new file mode 100644
index 0000000000..0c61d9db3c
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java
new file mode 100644
index 0000000000..bfcc38ad60
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java
new file mode 100644
index 0000000000..ab09c1de6d
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol class in frame header.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java
new file mode 100644
index 0000000000..6b819364da
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent format error in frame header.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java
new file mode 100644
index 0000000000..3165c373a9
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol instance in frame header.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java
new file mode 100644
index 0000000000..c9b0973ea6
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol version in frame header.
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
new file mode 100644
index 0000000000..a8e7f47db0
--- /dev/null
+++ b/RC9/qpid/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<AMQShortString>
+{
+ 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<Map<AMQShortString, WeakReference<AMQShortString>>> _localInternMap =
+ new ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>>()
+ {
+ protected Map<AMQShortString, WeakReference<AMQShortString>> initialValue()
+ {
+ return new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+ };
+ };
+
+ private static final Map<AMQShortString, WeakReference<AMQShortString>> _globalInternMap =
+ new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+
+ 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<AMQShortString, WeakReference<AMQShortString>> localMap =
+ _localInternMap.get();
+
+ WeakReference<AMQShortString> 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<AMQShortString> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java
new file mode 100644
index 0000000000..e2db8906a1
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java
new file mode 100644
index 0000000000..14fb63da03
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Get the equivalent one byte identifier for a type.
+ * <tr><td> Calculate the size of an instance of an AMQP parameter type. <td> {@link EncodingUtils}
+ * <tr><td> Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type.
+ * <td> {@link AMQTypedValue}
+ * <tr><td> Write an instance of an AMQP parameter type to a byte buffer. <td> {@link EncodingUtils}
+ * <tr><td> Read an instance of an AMQP parameter from a byte buffer. <td> {@link EncodingUtils}
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java
new file mode 100644
index 0000000000..a16e137466
--- /dev/null
+++ b/RC9/qpid/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<Byte, AMQType> _reverseTypeMap = new HashMap<Byte, AMQType>();
+
+ 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
new file mode 100644
index 0000000000..1ff39ca790
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create a fully typed AMQP value from a native type and a type tag. <td> {@link AMQType}
+ * <tr><td> Create a fully typed AMQP value from a binary representation in a byte buffer. <td> {@link AMQType}
+ * <tr><td> Write a fully typed AMQP value to a binary representation in a byte buffer. <td> {@link AMQType}
+ * <tr><td> Extract the type from a fully typed AMQP value.
+ * <tr><td> Extract the value from a fully typed AMQP value.
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java
new file mode 100644
index 0000000000..47b5c02beb
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java
new file mode 100644
index 0000000000..59646577e1
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java
new file mode 100644
index 0000000000..7162c37062
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
new file mode 100644
index 0000000000..94030f383e
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/Content.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/Content.java
new file mode 100644
index 0000000000..e5feeec2a4
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
new file mode 100644
index 0000000000..9d39f8aa86
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java
new file mode 100644
index 0000000000..c42995d148
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
new file mode 100644
index 0000000000..83e5a7e341
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java
new file mode 100644
index 0000000000..8d5e2f9fb4
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java
new file mode 100644
index 0000000000..7ef538cfdc
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java
new file mode 100644
index 0000000000..46189b63d7
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java
new file mode 100644
index 0000000000..f6795ff200
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java
new file mode 100644
index 0000000000..9cf96e698c
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
new file mode 100644
index 0000000000..6425f8c591
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
new file mode 100644
index 0000000000..ed01c91804
--- /dev/null
+++ b/RC9/qpid/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<AMQShortString, AMQTypedValue> _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<AMQShortString, AMQTypedValue>();
+ }
+ 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 <tt>null</tt> 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 <tt>null</tt> 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<AMQShortString, AMQTypedValue> 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<AMQShortString, AMQTypedValue> 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<String> keys()
+ {
+ initMapIfNecessary();
+ Set<String> keys = new LinkedHashSet<String>();
+ for (AMQShortString key : _properties.keySet())
+ {
+ keys.add(key.toString());
+ }
+
+ return keys;
+ }
+
+ public Iterator<Map.Entry<AMQShortString, AMQTypedValue>> 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<AMQShortString> 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<Map.Entry<AMQShortString, AMQTypedValue>> 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<AMQShortString, AMQTypedValue> 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<AMQShortString, AMQTypedValue>(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<AMQShortString, AMQTypedValue>
+ {
+ 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<Map.Entry<AMQShortString, AMQTypedValue>>
+ {
+
+ 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<AMQShortString, AMQTypedValue> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java
new file mode 100644
index 0000000000..e9d75137ef
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
new file mode 100644
index 0000000000..15a43345b5
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java
new file mode 100644
index 0000000000..c7ada708dc
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
new file mode 100644
index 0000000000..3ac17e9204
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java
new file mode 100644
index 0000000000..bd763599b0
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java
new file mode 100644
index 0000000000..76c154581d
--- /dev/null
+++ b/RC9/qpid/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<ProtocolVersionMethodConverter> versionMethodConverterClass =
+ (Class<ProtocolVersionMethodConverter>) 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java
new file mode 100644
index 0000000000..1d7c05e9cc
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java
new file mode 100644
index 0000000000..0695349f76
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java
new file mode 100644
index 0000000000..a96bdcc171
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java
new file mode 100644
index 0000000000..01d1a8a17b
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java
new file mode 100644
index 0000000000..0a1cedc4e6
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java b/RC9/qpid/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/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java b/RC9/qpid/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/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java b/RC9/qpid/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/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/RC9/qpid/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/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java
new file mode 100644
index 0000000000..5996cbf89c
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>These continuations are typically batched by {@link Job} for processing by a worker thread pool.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Process a continuation in the context of a Mina session.
+ * </table>
+ *
+ * @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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina messageReceived event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession}
+ * </table>
+ */
+ 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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina filterWrite event to a NextFilter.
+ * <td> {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession}
+ * </table>
+ */
+ 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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina sessionClosed event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession}
+ * </table>
+ */
+ 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java
new file mode 100644
index 0000000000..00da005515
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Aggregate many coninuations together into a single continuation.
+ * <tr><td> Sequentially process aggregated continuations. <td> {@link Event}
+ * <tr><td> Provide running and completion status of the aggregate continuation.
+ * <tr><td> Execute a terminal continuation upon job completion. <td> {@link JobCompletionHandler}
+ * </table>
+ *
+ * @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<Event> _eventQueue = new ConcurrentLinkedQueue<Event>();
+
+ /** 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 <tt>true</tt> if there are no enqueued continuations in this job, <tt>false</tt> otherwise.
+ */
+ public boolean isComplete()
+ {
+ return _eventQueue.peek() == null;
+ }
+
+ /**
+ * Marks this job as active if it is inactive. This method is thread safe.
+ *
+ * @return <tt>true</tt> if this job was inactive and has now been marked as active, <tt>false</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
new file mode 100644
index 0000000000..a080cc7e04
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>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.
+ *
+ * <p/>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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Implement default, pass through filter.
+ * <tr><td> Create pooling filters and a specific thread pool. <td> {@link ReferenceCountingExecutorService}
+ * <tr><td> Provide the ability to batch Mina events for asynchronous processing. <td> {@link Job}, {@link Event}
+ * <tr><td> Provide a terminal continuation to keep jobs running till empty.
+ * <td> {@link Job}, {@link Job.JobCompletionHandler}
+ * </table>
+ *
+ * @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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java
new file mode 100644
index 0000000000..8de0f93ce9
--- /dev/null
+++ b/RC9/qpid/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<Runnable> implements BlockingQueue<Runnable>
+{
+
+ 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<ReadWriteRunnable> _readJobQueue = new ConcurrentLinkedQueue<ReadWriteRunnable>();
+
+ private final ConcurrentLinkedQueue<ReadWriteRunnable> _writeJobQueue = new ConcurrentLinkedQueue<ReadWriteRunnable>();
+
+
+ private class ReadWriteJobIterator implements Iterator<Runnable>
+ {
+
+ private boolean _onReads;
+ private Iterator<ReadWriteRunnable> _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<Runnable> 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<? super Runnable> 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<? super Runnable> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java
new file mode 100644
index 0000000000..ad04a923e1
--- /dev/null
+++ b/RC9/qpid/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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java
new file mode 100644
index 0000000000..8cea70e597
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>Socket i/o will only happen with concurrent reads and writes if Mina has seperate selector threads for each.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create a filter chain with seperate read and write thread pools for read/write Mina events.
+ * <td> {@link PoolingFilter}
+ * </table>
+ */
+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/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java
new file mode 100644
index 0000000000..20a30b3ed3
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a shared exector service. <td> {@link Executors}
+ * <tr><td> Shutdown the executor service when not needed. <td> {@link ExecutorService}
+ * <tr><td> Track references to the executor service.
+ * <tr><td> Provide configuration of the executor service.
+ * </table>
+ *
+ * @todo Might be more elegant to make this actually implement ExecutorService, providing better hiding of the
+ * implementation details. Also this class introduces a pattern (albeit specific to this usage) that could be
+ * generalized to reference count anything. That is, on first instance call a create method, on release of last
+ * instance call a destroy method. This could definitely be abstracted out as a re-usable piece of code; a
+ * reference counting factory. It could then be re-used to do reference counting in other places (such as
+ * messages). Countable objects have a simple create/destroy life cycle, capturable by an interface that the
+ * ref counting factory can call to manage the lifecycle.
+ *
+ * @todo {@link #_poolSize} should be static?
+ *
+ * @todo The {@link #getPool()} method breaks the encapsulation of the reference counter. Generally when getPool is used
+ * further checks are applied to ensure that the exector service has not been shutdown. This passes responsibility
+ * for managing the lifecycle of the reference counted object onto the caller rather than neatly encapsulating it
+ * here. Could think about adding more state to the lifecycle, to mark ref counted objects as invalid, and have an
+ * isValid method, or could make calling code deal with RejectedExecutionException raised by shutdown executors.
+ */
+public class ReferenceCountingExecutorService
+{
+ /** Defines the smallest thread pool that will be allocated, irrespective of the number of processors. */
+ private static final int MINIMUM_POOL_SIZE = 4;
+
+ /** Holds the number of processors on the machine. */
+ private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors();
+
+ /** Defines the thread pool size to use, which is the larger of the number of CPUs or the minimum size. */
+ private static final int DEFAULT_POOL_SIZE = Math.max(NUM_CPUS, MINIMUM_POOL_SIZE);
+
+ /**
+ * Holds the singleton instance of this reference counter. This is only created once, statically, so the
+ * {@link #getInstance()} method does not need to be synchronized.
+ */
+ private static final ReferenceCountingExecutorService _instance = new ReferenceCountingExecutorService();
+
+ /** This lock is used to ensure that reference counts are updated atomically with create/destroy operations. */
+ private final Object _lock = new Object();
+
+ /** The shared executor service that is reference counted. */
+ private ExecutorService _pool;
+
+ /** Holds the number of references given out to the executor service. */
+ private int _refCount = 0;
+
+ /** Holds the number of executor threads to create. */
+ private int _poolSize = Integer.getInteger("amqj.read_write_pool_size", DEFAULT_POOL_SIZE);
+
+ private final boolean _useBiasedPool = Boolean.getBoolean("org.apache.qpid.use_write_biased_pool");
+
+ /**
+ * Retrieves the singleton instance of this reference counter.
+ *
+ * @return The singleton instance of this reference counter.
+ */
+ public static ReferenceCountingExecutorService getInstance()
+ {
+ return _instance;
+ }
+
+ /**
+ * Private constructor to ensure that only a singleton instance can be created.
+ */
+ private ReferenceCountingExecutorService()
+ { }
+
+ /**
+ * Provides a reference to a shared executor service, incrementing the reference count.
+ *
+ * @return An executor service.
+ */
+ public ExecutorService acquireExecutorService()
+ {
+ synchronized (_lock)
+ {
+ if (_refCount++ == 0)
+ {
+// _pool = Executors.newFixedThreadPool(_poolSize);
+
+ // Use a job queue that biases to writes
+ if(_useBiasedPool)
+ {
+ _pool = new ThreadPoolExecutor(_poolSize, _poolSize,
+ 0L, TimeUnit.MILLISECONDS,
+ new ReadWriteJobQueue());
+ }
+ else
+ {
+ _pool = Executors.newFixedThreadPool(_poolSize);
+ }
+ }
+
+
+ return _pool;
+ }
+ }
+
+ /**
+ * Releases a reference to a shared executor service, decrementing the reference count. If the refence count falls
+ * to zero, the executor service is shut down.
+ */
+ public void releaseExecutorService()
+ {
+ synchronized (_lock)
+ {
+ if (--_refCount == 0)
+ {
+ _pool.shutdownNow();
+ }
+ }
+ }
+
+ /**
+ * Provides access to the executor service, without touching the reference count.
+ *
+ * @return The shared executor service, or <tt>null</tt> 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/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
new file mode 100644
index 0000000000..8dee790a9e
--- /dev/null
+++ b/RC9/qpid/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.
+ *
+ * <p/><tabld id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Define the set of AMQP status codes.
+ * <tr><td> Provide a factory to lookup constants by their code.
+ * <tr><td>
+ */
+public final class AMQConstant
+{
+ /** Defines a map from codes to constants. */
+ private static Map _codeMap = new HashMap();
+
+ /** Indicates that the method completed successfully. */
+ public static final AMQConstant REPLY_SUCCESS = new AMQConstant(200, "reply success", true);
+
+ public static final AMQConstant FRAME_END = new AMQConstant(206, "frame end", true);
+
+ /**
+ * The client asked for a specific message that is no longer available. The message was delivered to another
+ * client, or was purged from the queue for some other reason.
+ */
+ public static final AMQConstant NOT_DELIVERED = new AMQConstant(310, "not delivered", true);
+
+ /**
+ * The client attempted to transfer content larger than the server could accept at the present time. The client
+ * may retry at a later time.
+ */
+ public static final AMQConstant MESSAGE_TOO_LARGE = new AMQConstant(311, "message too large", true);
+
+ /**
+ * When the exchange cannot route the result of a .Publish, most likely due to an invalid routing key. Only when
+ * the mandatory flag is set.
+ */
+ public static final AMQConstant NO_ROUTE = new AMQConstant(312, "no route", true);
+
+ /**
+ * When the exchange cannot deliver to a consumer when the immediate flag is set. As a result of pending data on
+ * the queue or the absence of any consumers of the queue.
+ */
+ public static final AMQConstant NO_CONSUMERS = new AMQConstant(313, "no consumers", true);
+
+ /**
+ * An operator intervened to close the connection for some reason. The client may retry at some later date.
+ */
+ public static final AMQConstant CONTEXT_IN_USE = new AMQConstant(320, "context in use", true);
+
+ /** The client tried to work with an unknown virtual host or cluster. */
+ public static final AMQConstant INVALID_PATH = new AMQConstant(402, "invalid path", true);
+
+ /** The client attempted to work with a server entity to which it has no access due to security settings. */
+ public static final AMQConstant ACCESS_REFUSED = new AMQConstant(403, "access refused", true);
+
+ /** The client attempted to work with a server entity that does not exist. */
+ public static final AMQConstant NOT_FOUND = new AMQConstant(404, "not found", true);
+
+ /**
+ * The client attempted to work with a server entity to which it has no access because another client is
+ * working with it.
+ */
+ public static final AMQConstant ALREADY_EXISTS = new AMQConstant(405, "Already exists", true);
+
+ /** The client requested a method that was not allowed because some precondition failed. */
+ public static final AMQConstant IN_USE = new AMQConstant(406, "In use", true);
+
+ public static final AMQConstant INVALID_ROUTING_KEY = new AMQConstant(407, "routing key invalid", true);
+
+ public static final AMQConstant REQUEST_TIMEOUT = new AMQConstant(408, "Request Timeout", true);
+
+ public static final AMQConstant INVALID_ARGUMENT = new AMQConstant(409, "argument invalid", true);
+
+ /**
+ * The client sent a malformed frame that the server could not decode. This strongly implies a programming error
+ * in the client.
+ */
+ public static final AMQConstant FRAME_ERROR = new AMQConstant(501, "frame error", true);
+
+ /**
+ * The client sent a frame that contained illegal values for one or more fields. This strongly implies a
+ * programming error in the client.
+ */
+ public static final AMQConstant SYNTAX_ERROR = new AMQConstant(502, "syntax error", true);
+
+ /**
+ * The client sent an invalid sequence of frames, attempting to perform an operation that was considered invalid
+ * by the server. This usually implies a programming error in the client.
+ */
+ public static final AMQConstant COMMAND_INVALID = new AMQConstant(503, "command invalid", true);
+
+ /**
+ * The client attempted to work with a channel that had not been correctly opened. This most likely indicates a
+ * fault in the client layer.
+ */
+ public static final AMQConstant CHANNEL_ERROR = new AMQConstant(504, "channel error", true);
+
+ /**
+ * The server could not complete the method because it lacked sufficient resources. This may be due to the client
+ * creating too many of some type of entity.
+ */
+ public static final AMQConstant RESOURCE_ERROR = new AMQConstant(506, "resource error", true);
+
+ /**
+ * The client tried to work with some entity in a manner that is prohibited by the server, due to security settings
+ * or by some other criteria.
+ */
+ public static final AMQConstant NOT_ALLOWED = new AMQConstant(530, "not allowed", true);
+
+ /** The client tried to use functionality that is not implemented in the server. */
+ public static final AMQConstant NOT_IMPLEMENTED = new AMQConstant(540, "not implemented", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant INTERNAL_ERROR = new AMQConstant(541, "internal error", true);
+
+ public static final AMQConstant FRAME_MIN_SIZE = new AMQConstant(4096, "frame min size", true);
+
+ /**
+ * The server does not support the protocol version
+ */
+ public static final AMQConstant UNSUPPORTED_BROKER_PROTOCOL_ERROR = new AMQConstant(542, "broker unsupported protocol", true);
+ /**
+ * The client imp does not support the protocol version
+ */
+ public static final AMQConstant UNSUPPORTED_CLIENT_PROTOCOL_ERROR = new AMQConstant(543, "client unsupported protocol", true);
+
+ /** The AMQP status code. */
+ private int _code;
+
+ /** A short description of the status code. */
+ private AMQShortString _name;
+
+ /**
+ * Creates a new AMQP status code.
+ *
+ * @param code The code.
+ * @param name A short description of the code.
+ * @param map <tt>true</tt> to register the code as a known code, <tt>false</tt> otherwise.
+ */
+ private AMQConstant(int code, String name, boolean map)
+ {
+ _code = code;
+ _name = new AMQShortString(name);
+ if (map)
+ {
+ _codeMap.put(new Integer(code), this);
+ }
+ }
+
+ /**
+ * Creates a constant for a status code by looking up the code in the map of known codes. If the code is not known
+ * a constant is still created for it, but it is marked as unknown.
+ *
+ * @param code The AMQP status code.
+ *
+ * @return The AMQP status code encapsulated as a constant.
+ */
+ public static AMQConstant getConstant(int code)
+ {
+ AMQConstant c = (AMQConstant) _codeMap.get(new Integer(code));
+ if (c == null)
+ {
+ c = new AMQConstant(code, "unknown code", false);
+ }
+
+ return c;
+ }
+
+ /**
+ * Gets the underlying AMQP status code.
+ *
+ * @return The AMQP status code.
+ */
+ public int getCode()
+ {
+ return _code;
+ }
+
+ /**
+ * Gets a short description of the status code.
+ *
+ * @return A short description of the status code.
+ */
+ public AMQShortString getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Renders the constant as a string, mainly for debugging purposes.
+ *
+ * @return The status code and its description.
+ */
+ public String toString()
+ {
+ return _code + ": " + _name;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java
new file mode 100644
index 0000000000..fd6907a152
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * AMQMethodEvent encapsulates an AMQP method call, and the channel on which that method call occurred.
+ *
+ * <p/>Supplies the:
+ * <ul>
+ * <li>channel id</li>
+ * <li>protocol method</li>
+ * </ul>
+ *
+ * <p/>As the event contains the context in which it occurred, event listeners do not need to be statefull.
+ * to listeners. Events are often handled by {@link AMQMethodListener}s.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Encapsulate an AMQP method call and the channel as the context for the method call.
+ * </table>
+ */
+public class AMQMethodEvent<M extends AMQMethodBody>
+{
+ /** Holds the method call. */
+ private final M _method;
+
+ /** Holds the channel handle for the method call. */
+ private final int _channelId;
+
+ /**
+ * Creates a method event to encasulate a method call and channel.
+ *
+ * @param channelId The channel on which the method call occurred.
+ * @param method The method call.
+ */
+ public AMQMethodEvent(int channelId, M method)
+ {
+ _channelId = channelId;
+ _method = method;
+ }
+
+ /**
+ * Gets the method call.
+ *
+ * @return The method call.
+ */
+ public M getMethod()
+ {
+ return _method;
+ }
+
+ /**
+ * Gets the channel handle for the method call.
+ *
+ * @return The channel handle for the method call.
+ */
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ /**
+ * Prints the method call as a string, mainly for debugging purposes.
+ *
+ * @return The method call as a string, mainly for debugging purposes.
+ */
+ public String toString()
+ {
+ StringBuilder buf = new StringBuilder("Method event: ");
+ buf.append("\nChannel id: ").append(_channelId);
+ buf.append("\nMethod: ").append(_method);
+
+ return buf.toString();
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
new file mode 100644
index 0000000000..5a7679a972
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.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.protocol;
+
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.AMQException;
+
+/**
+ * AMQMethodListener is a listener that receives notifications of AMQP methods. The methods are packaged as events in
+ * {@link AMQMethodEvent}.
+ *
+ * <p/>An event listener may be associated with a particular context, usually an AMQP channel, and in addition to
+ * receiving method events will be notified of errors on that context. This enables listeners to perform any clean
+ * up that they need to do before the context is closed or retried.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept notification of AMQP method events. <td> {@link AMQMethodEvent}
+ * <tr><td> Accept notification of errors on the event context.
+ * </table>
+ *
+ * @todo Document why the exception is passed to the error method. Is it so that the exception can be passed
+ * from the event handling thread to another thread and rethown from there? It is unusual to pass exceptions as
+ * method arguments, because they have their own mechanism for propagating through the call stack, so some
+ * explanation ought to be provided.
+ */
+public interface AMQMethodListener
+{
+ /**
+ * Notifies the listener that an AMQP method event has occurred.
+ *
+ * @param evt The AMQP method event (contains the method and channel).
+ *
+ * @return <tt>true</tt> if the handler processes the method frame, <tt>false<tt> otherwise. Note that this does
+ * not prohibit the method event being delivered to subsequent listeners but can be used to determine if
+ * nobody has dealt with an incoming method frame.
+ *
+ * @throws Exception if an error has occurred. This exception may be delivered to all registered listeners using
+ * the error() method (see below) allowing them to perform cleanup if necessary.
+ *
+ * @todo Consider narrowing the exception.
+ */
+ <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException;
+
+ /**
+ * Notifies the listener of an error on the event context to which it is listening. The listener should perform
+ * any necessary clean-up for the context.
+ *
+ * @param e The underlying exception that is the source of the error.
+ */
+ void error(Exception e);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java
new file mode 100644
index 0000000000..65884e4950
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.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.protocol;
+
+import org.apache.qpid.framing.AMQDataBlock;
+
+/**
+ * AMQProtocolWriter provides a method to write a frame of data 'to the wire', in the context of the object
+ * that implements the method, usually some sort of session. The block of data, encapsulated by {@link AMQDataBlock},
+ * will be encoded as it is written.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Write an encoded block of data to the write, in the context of a session.
+ * </table>
+ */
+public interface AMQProtocolWriter
+{
+ /**
+ * Writes a frame to the wire, encoding it as necessary, for example, into a sequence of bytes.
+ *
+ * @param frame The frame to be encoded and written.
+ */
+ public void writeFrame(AMQDataBlock frame);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java
new file mode 100644
index 0000000000..b58e7d01dc
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.AMQException;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to
+ * callers of the version of the AMQP protocol that they are able to work with.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide the method registry for a specific version of the AMQP.
+ * </table>
+ *
+ * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single
+ * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions
+ * implement AMQProtocolWriter directly and drop this interface.
+ */
+public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware
+{
+ /**
+ * Gets the method registry for a specific version of the AMQP.
+ *
+ * @return The method registry for a specific version of the AMQP.
+ */
+// public VersionSpecificRegistry getRegistry();
+
+ MethodRegistry getMethodRegistry();
+
+
+ public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException;
+ public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException;
+ public void contentBodyReceived(int channelId, ContentBody body) throws AMQException;
+ public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException;
+
+
+ public void setSender(Sender<ByteBuffer> sender);
+ public void init();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java
new file mode 100644
index 0000000000..56f950dd85
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.ProtocolVersion;
+
+/**
+ * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of
+ * the version of the AMQP protocol that they are able to handle.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Report the major and minor AMQP version handled.
+ * </table>
+ */
+public interface ProtocolVersionAware
+{
+ /**
+ * @deprecated
+ * Reports the AMQP minor version, that the implementer can handle.
+ *
+ * @return The AMQP minor version.
+ */
+ public byte getProtocolMinorVersion();
+
+ /**
+ * @deprecated
+ * Reports the AMQP major version, that the implementer can handle.
+ *
+ * @return The AMQP major version.
+ */
+ public byte getProtocolMajorVersion();
+
+ public ProtocolVersion getProtocolVersion();
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java
new file mode 100644
index 0000000000..a3dad9acdc
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security;
+
+import javax.security.auth.callback.CallbackHandler;
+
+public interface AMQPCallbackHandler extends CallbackHandler
+{
+ void initialise(String username,String password);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java
new file mode 100644
index 0000000000..8c80a1b5b7
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpid.QpidConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CallbackHandlerRegistry
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class);
+ private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry();
+
+ private Map<String,Class> _mechanismToHandlerClassMap = new HashMap<String,Class>();
+
+ private StringBuilder _mechanisms;
+
+ public static CallbackHandlerRegistry getInstance()
+ {
+ return _instance;
+ }
+
+ public Class getCallbackHandlerClass(String mechanism)
+ {
+ return _mechanismToHandlerClassMap.get(mechanism);
+ }
+
+ public String getMechanisms()
+ {
+ return _mechanisms.toString();
+ }
+
+ private CallbackHandlerRegistry()
+ {
+ // first we register any Sasl client factories
+ DynamicSaslRegistrar.registerSaslProviders();
+ registerMechanisms();
+ }
+
+ private void registerMechanisms()
+ {
+ for (QpidConfig.SecurityMechanism securityMechanism: QpidConfig.get().getSecurityMechanisms() )
+ {
+ Class clazz = null;
+ try
+ {
+ clazz = Class.forName(securityMechanism.getHandler());
+ if (!AMQPCallbackHandler.class.isAssignableFrom(clazz))
+ {
+ _logger.debug("SASL provider " + clazz + " does not implement " + AMQPCallbackHandler.class +
+ ". Skipping");
+ continue;
+ }
+ _mechanismToHandlerClassMap.put(securityMechanism.getType(), clazz);
+ if (_mechanisms == null)
+ {
+
+ _mechanisms = new StringBuilder();
+ _mechanisms.append(securityMechanism.getType());
+ }
+ else
+ {
+ _mechanisms.append(" " + securityMechanism.getType());
+ }
+ }
+ catch (ClassNotFoundException ex)
+ {
+ _logger.debug("Unable to load class " + securityMechanism.getHandler() + ". Skipping that SASL provider");
+ continue;
+ }
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java
new file mode 100644
index 0000000000..d2bf979d0a
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security;
+
+import java.security.Security;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.security.sasl.SaslClientFactory;
+
+import org.apache.qpid.QpidConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DynamicSaslRegistrar
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DynamicSaslRegistrar.class);
+
+ public static void registerSaslProviders()
+ {
+ Map<String, Class> factories = registerSaslClientFactories();
+ if (factories.size() > 0)
+ {
+ if (Security.addProvider(new JCAProvider(factories)) == -1)
+ {
+ _logger.error("Unable to load custom SASL providers. Qpid custom SASL authenticators unavailable.");
+ }
+ else
+ {
+ _logger.debug("Dynamic SASL provider added as a security provider");
+ }
+ }
+ }
+
+ private static Map<String, Class> registerSaslClientFactories()
+ {
+ TreeMap<String, Class> factoriesToRegister =
+ new TreeMap<String, Class>();
+
+ for (QpidConfig.SaslClientFactory factory: QpidConfig.get().getSaslClientFactories())
+ {
+ String className = factory.getFactoryClass();
+ try
+ {
+ Class clazz = Class.forName(className);
+ if (!(SaslClientFactory.class.isAssignableFrom(clazz)))
+ {
+ _logger.debug("Class " + clazz + " does not implement " + SaslClientFactory.class + " - skipping");
+ continue;
+ }
+ factoriesToRegister.put(factory.getType(), clazz);
+ }
+ catch (Exception ex)
+ {
+ _logger.debug("Error instantiating SaslClientFactory calss " + className + " - skipping");
+ }
+ }
+ return factoriesToRegister;
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java
new file mode 100644
index 0000000000..033deb550c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/JCAProvider.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.security;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.Map;
+
+public class JCAProvider extends Provider
+{
+ public JCAProvider(Map<String, Class> providerMap)
+ {
+ super("AMQSASLProvider", 1.0, "A JCA provider that registers all " +
+ "AMQ SASL providers that want to be registered");
+ register(providerMap);
+ Security.addProvider(this);
+ }
+
+ private void register(Map<String, Class> providerMap)
+ {
+ for (Map.Entry<String, Class> me :providerMap.entrySet())
+ {
+ put("SaslClientFactory." + me.getKey(), me.getValue().getName());
+ }
+ }
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java
new file mode 100644
index 0000000000..89a63abeab
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class UsernamePasswordCallbackHandler implements AMQPCallbackHandler
+{
+ private String _username;
+ private String _password;
+
+ public void initialise(String username,String password)
+ {
+ _username = username;
+ _password = password;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ Callback cb = callbacks[i];
+ if (cb instanceof NameCallback)
+ {
+ ((NameCallback)cb).setName(_username);
+ }
+ else if (cb instanceof PasswordCallback)
+ {
+ ((PasswordCallback)cb).setPassword((_password).toCharArray());
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(cb);
+ }
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java
new file mode 100644
index 0000000000..81acc66de4
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security.amqplain;
+
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.Callback;
+
+/**
+ * Implements the "AMQPlain" authentication protocol that uses FieldTables to send username and pwd.
+ *
+ */
+public class AmqPlainSaslClient implements SaslClient
+{
+ /**
+ * The name of this mechanism
+ */
+ public static final String MECHANISM = "AMQPLAIN";
+
+ private CallbackHandler _cbh;
+
+ public AmqPlainSaslClient(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return "AMQPLAIN";
+ }
+
+ public boolean hasInitialResponse()
+ {
+ return true;
+ }
+
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException
+ {
+ // we do not care about the prompt or the default name
+ NameCallback nameCallback = new NameCallback("prompt", "defaultName");
+ PasswordCallback pwdCallback = new PasswordCallback("prompt", false);
+ Callback[] callbacks = new Callback[]{nameCallback, pwdCallback};
+ try
+ {
+ _cbh.handle(callbacks);
+ }
+ catch (Exception e)
+ {
+ throw new SaslException("Error handling SASL callbacks: " + e, e);
+ }
+ FieldTable table = FieldTableFactory.newFieldTable();
+ table.setString("LOGIN", nameCallback.getName());
+ table.setString("PASSWORD", new String(pwdCallback.getPassword()));
+ return table.getDataAsBytes();
+ }
+
+ public boolean isComplete()
+ {
+ return true;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Not supported");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Not supported");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java
new file mode 100644
index 0000000000..6c66c87f4c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.security.amqplain;
+
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Map;
+
+public class AmqPlainSaslClientFactory implements SaslClientFactory
+{
+ public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh) throws SaslException
+ {
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ if (mechanisms[i].equals(AmqPlainSaslClient.MECHANISM))
+ {
+ if (cbh == null)
+ {
+ throw new SaslException("CallbackHandler must not be null");
+ }
+ return new AmqPlainSaslClient(cbh);
+ }
+ }
+ return null;
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{AmqPlainSaslClient.MECHANISM};
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java
new file mode 100644
index 0000000000..e142d21e06
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.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.ssl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Factory used to create SSLContexts. SSL needs to be configured
+ * before this will work.
+ *
+ */
+public class SSLContextFactory {
+
+ /**
+ * Path to the Java keystore file
+ */
+ private String _keyStorePath;
+
+ /**
+ * Password for the keystore
+ */
+ private String _keyStorePassword;
+
+ /**
+ * Cert type to use in keystore
+ */
+ private String _keyStoreCertType;
+
+ /**
+ * Path to the Java truststore file
+ */
+ private String _trustStorePath;
+
+ /**
+ * Password for the truststore
+ */
+ private String _trustStorePassword;
+
+ /**
+ * Cert type to use in truststore
+ */
+ private String _trustStoreCertType;
+
+
+
+ public SSLContextFactory(String trustStorePath, String trustStorePassword,
+ String trustStoreCertType)
+ {
+ this(trustStorePath,trustStorePassword,trustStoreCertType,
+ trustStorePath,trustStorePassword,trustStoreCertType);
+ }
+
+ /**
+ * Create a factory instance
+ * @param keystorePath path to the Java keystore file
+ * @param keystorePassword password for the Java keystore
+ * @param certType certificate type
+ */
+ public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType,
+ String keyStorePath, String keyStorePassword, String keyStoreCertType)
+ {
+
+ _trustStorePath = trustStorePath;
+ _trustStorePassword = trustStorePassword;
+
+ if (_trustStorePassword.equals("none"))
+ {
+ _trustStorePassword = null;
+ }
+ _trustStoreCertType = trustStoreCertType;
+
+ _keyStorePath = keyStorePath;
+ _keyStorePassword = keyStorePassword;
+
+ if (_keyStorePassword.equals("none"))
+ {
+ _keyStorePassword = null;
+ }
+ _keyStoreCertType = keyStoreCertType;
+
+ if (_trustStorePath == null) {
+ throw new IllegalArgumentException("A TrustStore path or KeyStore path must be specified");
+ }
+ if (_trustStoreCertType == null) {
+ throw new IllegalArgumentException("Cert type must be specified");
+ }
+ }
+
+ /**
+ * Builds a SSLContext appropriate for use with a server
+ * @return SSLContext
+ * @throws GeneralSecurityException
+ * @throws IOException
+ */
+ public SSLContext buildServerContext() throws GeneralSecurityException, IOException
+ {
+ // Create keystore
+ KeyStore ks = getInitializedKeyStore(_keyStorePath,_keyStorePassword);
+
+ // Set up key manager factory to use our key store
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType);
+ kmf.init(ks, _keyStorePassword.toCharArray());
+
+ KeyStore ts = getInitializedKeyStore(_trustStorePath,_trustStorePassword);
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType);
+ tmf.init(ts);
+
+ // Initialize the SSLContext to work with our key managers.
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+ return sslContext;
+ }
+
+ /**
+ * Creates a SSLContext factory appropriate for use with a client
+ * @return SSLContext
+ * @throws GeneralSecurityException
+ * @throws IOException
+ */
+ public SSLContext buildClientContext() throws GeneralSecurityException, IOException
+ {
+ KeyStore ks = getInitializedKeyStore(_trustStorePath,_trustStorePassword);
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType);
+ tmf.init(ks);
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+ return context;
+ }
+
+ private KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException
+ {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ InputStream in = null;
+ try
+ {
+ File f = new File(storePath);
+ if (f.exists())
+ {
+ in = new FileInputStream(f);
+ }
+ else
+ {
+ in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath);
+ }
+ if (in == null)
+ {
+ throw new IOException("Unable to load keystore resource: " + storePath);
+ }
+ ks.load(in, storePassword.toCharArray());
+ }
+ finally
+ {
+ if (in != null)
+ {
+ //noinspection EmptyCatchBlock
+ try
+ {
+ in.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ return ks;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binary.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binary.java
new file mode 100644
index 0000000000..4e97855a6f
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binary.java
@@ -0,0 +1,145 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.nio.ByteBuffer;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+
+/**
+ * Binary
+ *
+ */
+
+public final class Binary
+{
+
+ private byte[] bytes;
+ private int offset;
+ private int size;
+ private int hash = 0;
+
+ public Binary(byte[] bytes, int offset, int size)
+ {
+ if (offset + size > bytes.length)
+ {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ this.bytes = bytes;
+ this.offset = offset;
+ this.size = size;
+ }
+
+ public Binary(byte[] bytes)
+ {
+ this(bytes, 0, bytes.length);
+ }
+
+ public final byte[] getBytes()
+ {
+ byte[] result = new byte[size];
+ System.arraycopy(bytes, offset, result, 0, size);
+ return result;
+ }
+
+ public final byte[] array()
+ {
+ return bytes;
+ }
+
+ public final int offset()
+ {
+ return offset;
+ }
+
+ public final int size()
+ {
+ return size;
+ }
+
+ public final Binary slice(int low, int high)
+ {
+ int sz;
+
+ if (high < 0)
+ {
+ sz = size + high;
+ }
+ else
+ {
+ sz = high - low;
+ }
+
+ if (sz < 0)
+ {
+ sz = 0;
+ }
+
+ return new Binary(bytes, offset + low, sz);
+ }
+
+ public final int hashCode()
+ {
+ if (hash == 0)
+ {
+ int hc = 0;
+ for (int i = 0; i < size; i++)
+ {
+ hc = 31*hc + (0xFF & bytes[offset + i]);
+ }
+ hash = hc;
+ }
+
+ return hash;
+ }
+
+ public final boolean equals(Object o)
+ {
+ if (!(o instanceof Binary))
+ {
+ return false;
+ }
+
+ Binary buf = (Binary) o;
+ if (this.size != buf.size)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < size; i++)
+ {
+ if (bytes[offset + i] != buf.bytes[buf.offset + i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String toString()
+ {
+ return str(ByteBuffer.wrap(bytes, offset, size));
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binding.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binding.java
new file mode 100644
index 0000000000..8418c42189
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Binding.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+
+/**
+ * Binding
+ *
+ */
+
+public interface Binding<E,T>
+{
+
+ E endpoint(Sender<T> sender);
+
+ Receiver<T> receiver(E endpoint);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java
new file mode 100644
index 0000000000..2604f6970c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.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.transport;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.qpid.QpidException;
+
+import org.apache.qpid.security.UsernamePasswordCallbackHandler;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+
+import static org.apache.qpid.transport.Connection.State.*;
+
+
+/**
+ * ClientDelegate
+ *
+ */
+
+public class ClientDelegate extends ConnectionDelegate
+{
+
+ private String vhost;
+ private String username;
+ private String password;
+
+ public ClientDelegate(String vhost, String username, String password)
+ {
+ this.vhost = vhost;
+ this.username = username;
+ this.password = password;
+ }
+
+ public void init(Connection conn, ProtocolHeader hdr)
+ {
+ if (!(hdr.getMajor() == 0 && hdr.getMinor() == 10))
+ {
+ conn.exception(new ProtocolVersionException(hdr.getMajor(), hdr.getMinor()));
+ }
+ }
+
+ @Override public void connectionStart(Connection conn, ConnectionStart start)
+ {
+ List<Object> mechanisms = start.getMechanisms();
+ if (mechanisms == null || mechanisms.isEmpty())
+ {
+ conn.connectionStartOk
+ (Collections.EMPTY_MAP, null, null, conn.getLocale());
+ return;
+ }
+
+ String[] mechs = new String[mechanisms.size()];
+ mechanisms.toArray(mechs);
+
+ try
+ {
+ UsernamePasswordCallbackHandler handler =
+ new UsernamePasswordCallbackHandler();
+ handler.initialise(username, password);
+ SaslClient sc = Sasl.createSaslClient
+ (new String[] {"PLAIN"}, null, "AMQP", "localhost", null, handler);
+ conn.setSaslClient(sc);
+
+ byte[] response = sc.hasInitialResponse() ?
+ sc.evaluateChallenge(new byte[0]) : null;
+ conn.connectionStartOk
+ (Collections.EMPTY_MAP, sc.getMechanismName(), response,
+ conn.getLocale());
+ }
+ catch (SaslException e)
+ {
+ conn.exception(e);
+ }
+ }
+
+ @Override public void connectionSecure(Connection conn, ConnectionSecure secure)
+ {
+ SaslClient sc = conn.getSaslClient();
+ try
+ {
+ byte[] response = sc.evaluateChallenge(secure.getChallenge());
+ conn.connectionSecureOk(response);
+ }
+ catch (SaslException e)
+ {
+ conn.exception(e);
+ }
+ }
+
+ @Override public void connectionTune(Connection conn, ConnectionTune tune)
+ {
+ conn.setChannelMax(tune.getChannelMax());
+ conn.connectionTuneOk(tune.getChannelMax(), tune.getMaxFrameSize(), tune.getHeartbeatMax());
+ conn.connectionOpen(vhost, null, Option.INSIST);
+ }
+
+ @Override public void connectionOpenOk(Connection conn, ConnectionOpenOk ok)
+ {
+ conn.setState(OPEN);
+ }
+
+ @Override public void connectionRedirect(Connection conn, ConnectionRedirect redir)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java
new file mode 100644
index 0000000000..56cbf5ee13
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java
@@ -0,0 +1,505 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import org.apache.qpid.transport.network.ConnectionBinding;
+import org.apache.qpid.transport.network.io.IoTransport;
+import org.apache.qpid.transport.util.Logger;
+import org.apache.qpid.transport.util.Waiter;
+import org.apache.qpid.util.Strings;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.UUID;
+
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslServer;
+
+import static org.apache.qpid.transport.Connection.State.*;
+
+
+/**
+ * Connection
+ *
+ * @author Rafael H. Schloming
+ *
+ * @todo the channels map should probably be replaced with something
+ * more efficient, e.g. an array or a map implementation that can use
+ * short instead of Short
+ */
+
+public class Connection extends ConnectionInvoker
+ implements Receiver<ProtocolEvent>, Sender<ProtocolEvent>
+{
+
+ private static final Logger log = Logger.get(Connection.class);
+
+ enum State { NEW, CLOSED, OPENING, OPEN, CLOSING, CLOSE_RCVD }
+
+ class DefaultConnectionListener implements ConnectionListener
+ {
+ public void opened(Connection conn) {}
+ public void exception(Connection conn, ConnectionException exception)
+ {
+ log.error(exception, "connection exception");
+ }
+ public void closed(Connection conn) {}
+ }
+
+ private ConnectionDelegate delegate;
+ private Sender<ProtocolEvent> sender;
+
+ final private Map<Binary,Session> sessions = new HashMap<Binary,Session>();
+ final private Map<Integer,Session> channels = new HashMap<Integer,Session>();
+
+ private State state = NEW;
+ private Object lock = new Object();
+ private long timeout = 60000;
+ private ConnectionListener listener = new DefaultConnectionListener();
+ private ConnectionException error = null;
+
+ private int channelMax = 1;
+ private String locale;
+ private SaslServer saslServer;
+ private SaslClient saslClient;
+
+ // want to make this final
+ private int _connectionId;
+
+ public Connection() {}
+
+ public void setConnectionDelegate(ConnectionDelegate delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ public void setConnectionListener(ConnectionListener listener)
+ {
+ if (listener == null)
+ {
+ this.listener = new DefaultConnectionListener();
+ }
+ else
+ {
+ this.listener = listener;
+ }
+ }
+
+ public Sender<ProtocolEvent> getSender()
+ {
+ return sender;
+ }
+
+ public void setSender(Sender<ProtocolEvent> sender)
+ {
+ this.sender = sender;
+ }
+
+ void setState(State state)
+ {
+ synchronized (lock)
+ {
+ this.state = state;
+ lock.notifyAll();
+ }
+ }
+
+ void setLocale(String locale)
+ {
+ this.locale = locale;
+ }
+
+ String getLocale()
+ {
+ return locale;
+ }
+
+ void setSaslServer(SaslServer saslServer)
+ {
+ this.saslServer = saslServer;
+ }
+
+ SaslServer getSaslServer()
+ {
+ return saslServer;
+ }
+
+ void setSaslClient(SaslClient saslClient)
+ {
+ this.saslClient = saslClient;
+ }
+
+ SaslClient getSaslClient()
+ {
+ return saslClient;
+ }
+
+ public void connect(String host, int port, String vhost, String username, String password)
+ {
+ connect(host, port, vhost, username, password, false);
+ }
+
+ public void connect(String host, int port, String vhost, String username, String password, boolean ssl)
+ {
+ synchronized (lock)
+ {
+ state = OPENING;
+
+ delegate = new ClientDelegate(vhost, username, password);
+
+ IoTransport.connect(host, port, ConnectionBinding.get(this), ssl);
+ send(new ProtocolHeader(1, 0, 10));
+
+ Waiter w = new Waiter(lock, timeout);
+ while (w.hasTime() && state == OPENING && error == null)
+ {
+ w.await();
+ }
+
+ if (error != null)
+ {
+ ConnectionException t = error;
+ error = null;
+ try
+ {
+ close();
+ }
+ catch (ConnectionException ce)
+ {
+ if (!(t instanceof ProtocolVersionException))
+ {
+ throw ce;
+ }
+ }
+ t.rethrow();
+ }
+
+ switch (state)
+ {
+ case OPENING:
+ close();
+ throw new ConnectionException("connect() timed out");
+ case OPEN:
+ break;
+ case CLOSED:
+ throw new ConnectionException("connect() aborted");
+ default:
+ throw new IllegalStateException(String.valueOf(state));
+ }
+ }
+
+ listener.opened(this);
+ }
+
+ public Session createSession()
+ {
+ return createSession(0);
+ }
+
+ public Session createSession(long expiry)
+ {
+ return createSession(UUID.randomUUID().toString(), expiry);
+ }
+
+ public Session createSession(String name)
+ {
+ return createSession(name, 0);
+ }
+
+ public Session createSession(String name, long expiry)
+ {
+ return createSession(Strings.toUTF8(name), expiry);
+ }
+
+ public Session createSession(byte[] name, long expiry)
+ {
+ return createSession(new Binary(name), expiry);
+ }
+
+ public Session createSession(Binary name, long expiry)
+ {
+ synchronized (lock)
+ {
+ Session ssn = new Session(this, name, expiry);
+ sessions.put(name, ssn);
+ map(ssn);
+ ssn.attach();
+ return ssn;
+ }
+ }
+
+ void removeSession(Session ssn)
+ {
+ synchronized (lock)
+ {
+ sessions.remove(ssn.getName());
+ }
+ }
+
+ public void setConnectionId(int id)
+ {
+ _connectionId = id;
+ }
+
+ public int getConnectionId()
+ {
+ return _connectionId;
+ }
+
+ public ConnectionDelegate getConnectionDelegate()
+ {
+ return delegate;
+ }
+
+ public void received(ProtocolEvent event)
+ {
+ log.debug("RECV: [%s] %s", this, event);
+ event.delegate(this, delegate);
+ }
+
+ public void send(ProtocolEvent event)
+ {
+ log.debug("SEND: [%s] %s", this, event);
+ Sender s = sender;
+ if (s == null)
+ {
+ throw new ConnectionException("connection closed");
+ }
+ s.send(event);
+ }
+
+ public void flush()
+ {
+ log.debug("FLUSH: [%s]", this);
+ sender.flush();
+ }
+
+ protected void invoke(Method method)
+ {
+ method.setChannel(0);
+ send(method);
+ if (!method.isBatch())
+ {
+ flush();
+ }
+ }
+
+ public void dispatch(Method method)
+ {
+ Session ssn = getSession(method.getChannel());
+ ssn.received(method);
+ }
+
+ public int getChannelMax()
+ {
+ return channelMax;
+ }
+
+ void setChannelMax(int max)
+ {
+ channelMax = max;
+ }
+
+ private int map(Session ssn)
+ {
+ synchronized (lock)
+ {
+ for (int i = 0; i < getChannelMax(); i++)
+ {
+ if (!channels.containsKey(i))
+ {
+ map(ssn, i);
+ return i;
+ }
+ }
+
+ throw new RuntimeException("no more channels available");
+ }
+ }
+
+ void map(Session ssn, int channel)
+ {
+ synchronized (lock)
+ {
+ channels.put(channel, ssn);
+ ssn.setChannel(channel);
+ }
+ }
+
+ void unmap(Session ssn)
+ {
+ synchronized (lock)
+ {
+ channels.remove(ssn.getChannel());
+ }
+ }
+
+ Session getSession(int channel)
+ {
+ synchronized (lock)
+ {
+ return channels.get(channel);
+ }
+ }
+
+ public void resume()
+ {
+ synchronized (lock)
+ {
+ for (Session ssn : sessions.values())
+ {
+ map(ssn);
+ ssn.attach();
+ ssn.resume();
+ }
+ }
+ }
+
+ public void exception(ConnectionException e)
+ {
+ synchronized (lock)
+ {
+ switch (state)
+ {
+ case OPENING:
+ case CLOSING:
+ error = e;
+ lock.notifyAll();
+ return;
+ }
+ }
+
+ listener.exception(this, e);
+ }
+
+ public void exception(Throwable t)
+ {
+ exception(new ConnectionException(t));
+ }
+
+ void closeCode(ConnectionClose close)
+ {
+ synchronized (lock)
+ {
+ for (Session ssn : channels.values())
+ {
+ ssn.closeCode(close);
+ }
+ ConnectionCloseCode code = close.getReplyCode();
+ if (code != ConnectionCloseCode.NORMAL)
+ {
+ exception(new ConnectionException(close));
+ }
+ }
+ }
+
+ public void closed()
+ {
+ if (state == OPEN)
+ {
+ exception(new ConnectionException("connection aborted"));
+ }
+
+ log.debug("connection closed: %s", this);
+
+ synchronized (lock)
+ {
+ List<Session> values = new ArrayList<Session>(channels.values());
+ for (Session ssn : values)
+ {
+ ssn.closed();
+ }
+
+ sender = null;
+ setState(CLOSED);
+ }
+
+ listener.closed(this);
+ }
+
+ public void close()
+ {
+ synchronized (lock)
+ {
+ switch (state)
+ {
+ case OPEN:
+ state = CLOSING;
+ connectionClose(ConnectionCloseCode.NORMAL, null);
+ Waiter w = new Waiter(lock, timeout);
+ while (w.hasTime() && state == CLOSING && error == null)
+ {
+ w.await();
+ }
+
+ if (error != null)
+ {
+ close();
+ throw new ConnectionException(error);
+ }
+
+ switch (state)
+ {
+ case CLOSING:
+ close();
+ throw new ConnectionException("close() timed out");
+ case CLOSED:
+ break;
+ default:
+ throw new IllegalStateException(String.valueOf(state));
+ }
+ break;
+ case CLOSED:
+ break;
+ default:
+ if (sender != null)
+ {
+ sender.close();
+ w = new Waiter(lock, timeout);
+ while (w.hasTime() && sender != null && error == null)
+ {
+ w.await();
+ }
+
+ if (error != null)
+ {
+ throw new ConnectionException(error);
+ }
+
+ if (sender != null)
+ {
+ throw new ConnectionException("close() timed out");
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return String.format("conn:%x", System.identityHashCode(this));
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java
new file mode 100644
index 0000000000..a1e3dee024
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.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.transport;
+
+import org.apache.qpid.transport.util.Logger;
+
+import static org.apache.qpid.transport.Connection.State.*;
+
+
+/**
+ * ConnectionDelegate
+ *
+ * @author Rafael H. Schloming
+ */
+
+/**
+ * Currently only implemented client specific methods
+ * the server specific methods are dummy impls for testing
+ *
+ * the connectionClose is kind of different for both sides
+ */
+public abstract class ConnectionDelegate
+ extends MethodDelegate<Connection>
+ implements ProtocolDelegate<Connection>
+{
+
+ private static final Logger log = Logger.get(ConnectionDelegate.class);
+
+ public void control(Connection conn, Method method)
+ {
+ method.dispatch(conn, this);
+ }
+
+ public void command(Connection conn, Method method)
+ {
+ method.dispatch(conn, this);
+ }
+
+ public void error(Connection conn, ProtocolError error)
+ {
+ conn.exception(new ConnectionException(error.getMessage()));
+ }
+
+ public void handle(Connection conn, Method method)
+ {
+ conn.dispatch(method);
+ }
+
+ @Override public void connectionClose(Connection conn, ConnectionClose close)
+ {
+ conn.connectionCloseOk();
+ conn.getSender().close();
+ conn.closeCode(close);
+ conn.setState(CLOSE_RCVD);
+ }
+
+ @Override public void connectionCloseOk(Connection conn, ConnectionCloseOk ok)
+ {
+ conn.getSender().close();
+ }
+
+ @Override public void sessionDetach(Connection conn, SessionDetach dtc)
+ {
+ Session ssn = conn.getSession(dtc.getChannel());
+ conn.unmap(ssn);
+ ssn.sessionDetached(dtc.getName(), SessionDetachCode.NORMAL);
+ ssn.closed();
+ }
+
+ @Override public void sessionDetached(Connection conn, SessionDetached dtc)
+ {
+ Session ssn = conn.getSession(dtc.getChannel());
+ if (ssn != null)
+ {
+ conn.unmap(ssn);
+ ssn.closed();
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java
new file mode 100644
index 0000000000..6d3972eb43
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.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.transport;
+
+
+/**
+ * ConnectionException
+ *
+ */
+
+public class ConnectionException extends TransportException
+{
+
+ private ConnectionClose close;
+
+ public ConnectionException(String message, ConnectionClose close, Throwable cause)
+ {
+ super(message, cause);
+ this.close = close;
+ }
+
+ public ConnectionException(String message)
+ {
+ this(message, null, null);
+ }
+
+ public ConnectionException(String message, Throwable cause)
+ {
+ this(message, null, cause);
+ }
+
+ public ConnectionException(Throwable cause)
+ {
+ this(cause.getMessage(), null, cause);
+ }
+
+ public ConnectionException(ConnectionClose close)
+ {
+ this(close.getReplyText(), close, null);
+ }
+
+ public ConnectionClose getClose()
+ {
+ return close;
+ }
+
+ @Override public void rethrow()
+ {
+ throw new ConnectionException(getMessage(), close, this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java
new file mode 100644
index 0000000000..616e76825a
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.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.transport;
+
+
+/**
+ * ConnectionListener
+ *
+ */
+
+public interface ConnectionListener
+{
+
+ void opened(Connection connection);
+
+ void exception(Connection connection, ConnectionException exception);
+
+ void closed(Connection connection);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Echo.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Echo.java
new file mode 100644
index 0000000000..c1031c9a1c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Echo.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.transport;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.transport.network.ConnectionBinding;
+import org.apache.qpid.transport.network.io.IoAcceptor;
+
+
+/**
+ * Echo
+ *
+ */
+
+public class Echo implements SessionListener
+{
+
+ public void opened(Session ssn) {}
+
+ public void message(Session ssn, MessageTransfer xfr)
+ {
+ int id = xfr.getId();
+ ssn.invoke(xfr);
+ ssn.processed(id);
+ }
+
+ public void exception(Session ssn, SessionException exc)
+ {
+ exc.printStackTrace();
+ }
+
+ public void closed(Session ssn) {}
+
+ public static final void main(String[] args) throws IOException
+ {
+ ConnectionDelegate delegate = new ServerDelegate()
+ {
+ @Override public Session getSession(Connection conn, SessionAttach atc)
+ {
+ Session ssn = super.getSession(conn, atc);
+ ssn.setSessionListener(new Echo());
+ return ssn;
+ }
+ };
+
+ IoAcceptor ioa = new IoAcceptor
+ ("0.0.0.0", 5672, ConnectionBinding.get(delegate));
+ ioa.start();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Field.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Field.java
new file mode 100644
index 0000000000..bc6bf10041
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Field.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.transport;
+
+import org.apache.qpid.transport.codec.Decoder;
+import org.apache.qpid.transport.codec.Encoder;
+
+
+/**
+ * Field
+ *
+ */
+
+public abstract class Field<C,T>
+{
+
+ private final Class<C> container;
+ private final Class<T> type;
+ private final String name;
+ private final int index;
+
+ Field(Class<C> container, Class<T> type, String name, int index)
+ {
+ this.container = container;
+ this.type = type;
+ this.name = name;
+ this.index = index;
+ }
+
+ public final Class<C> getContainer()
+ {
+ return container;
+ }
+
+ public final Class<T> getType()
+ {
+ return type;
+ }
+
+ public final String getName()
+ {
+ return name;
+ }
+
+ public final int getIndex()
+ {
+ return index;
+ }
+
+ protected final C check(Object struct)
+ {
+ return container.cast(struct);
+ }
+
+ public abstract boolean has(Object struct);
+
+ public abstract void has(Object struct, boolean value);
+
+ public abstract T get(Object struct);
+
+ public abstract void read(Decoder dec, Object struct);
+
+ public abstract void write(Encoder enc, Object struct);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Future.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Future.java
new file mode 100644
index 0000000000..d8cde61af5
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Future.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+
+/**
+ * Future
+ *
+ * @author Rafael H. Schloming
+ */
+
+public interface Future<T>
+{
+
+ T get();
+
+ boolean isDone();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Header.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Header.java
new file mode 100644
index 0000000000..9439e5e0de
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Header.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import org.apache.qpid.transport.network.Frame;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.nio.ByteBuffer;
+
+
+/**
+ * Header
+ *
+ * @author Rafael H. Schloming
+ */
+
+public class Header {
+
+ private final Struct[] structs;
+
+ public Header(List<Struct> structs)
+ {
+ this(structs.toArray(new Struct[structs.size()]));
+ }
+
+ public Header(Struct ... structs)
+ {
+ this.structs = structs;
+ }
+
+ public Struct[] getStructs()
+ {
+ return structs;
+ }
+
+
+ public <T> T get(Class<T> klass)
+ {
+ for (Struct st : structs)
+ {
+ if (klass.isInstance(st))
+ {
+ return (T) st;
+ }
+ }
+
+ return null;
+ }
+
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer();
+ str.append(" Header(");
+ boolean first = true;
+ for (Struct s : structs)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ str.append(", ");
+ }
+ str.append(s);
+ }
+ str.append(")");
+ return str.toString();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Method.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Method.java
new file mode 100644
index 0000000000..6b99f6d5d3
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Method.java
@@ -0,0 +1,180 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import org.apache.qpid.transport.network.Frame;
+
+import java.nio.ByteBuffer;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+/**
+ * Method
+ *
+ * @author Rafael H. Schloming
+ */
+
+public abstract class Method extends Struct implements ProtocolEvent
+{
+
+ public static final Method create(int type)
+ {
+ // XXX: should generate separate factories for separate
+ // namespaces
+ return (Method) StructFactory.createInstruction(type);
+ }
+
+ // XXX: command subclass?
+ private int id;
+ private int channel;
+ private boolean idSet = false;
+ private boolean sync = false;
+ private boolean batch = false;
+
+ public final int getId()
+ {
+ return id;
+ }
+
+ void setId(int id)
+ {
+ this.id = id;
+ this.idSet = true;
+ }
+
+ public final int getChannel()
+ {
+ return channel;
+ }
+
+ public final void setChannel(int channel)
+ {
+ this.channel = channel;
+ }
+
+ public final boolean isSync()
+ {
+ return sync;
+ }
+
+ final void setSync(boolean value)
+ {
+ this.sync = value;
+ }
+
+ public final boolean isBatch()
+ {
+ return batch;
+ }
+
+ final void setBatch(boolean value)
+ {
+ this.batch = value;
+ }
+
+ public abstract boolean hasPayload();
+
+ public Header getHeader()
+ {
+ return null;
+ }
+
+ public void setHeader(Header header)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ByteBuffer getBody()
+ {
+ return null;
+ }
+
+ public void setBody(ByteBuffer body)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public abstract byte getEncodedTrack();
+
+ public abstract <C> void dispatch(C context, MethodDelegate<C> delegate);
+
+ public <C> void delegate(C context, ProtocolDelegate<C> delegate)
+ {
+ if (getEncodedTrack() == Frame.L4)
+ {
+ delegate.command(context, this);
+ }
+ else
+ {
+ delegate.control(context, this);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+
+ str.append("ch=");
+ str.append(channel);
+
+ if (getEncodedTrack() == Frame.L4 && idSet)
+ {
+ str.append(" id=");
+ str.append(id);
+ }
+
+ if (sync || batch)
+ {
+ str.append(" ");
+ str.append("[");
+ if (sync)
+ {
+ str.append("S");
+ }
+ if (batch)
+ {
+ str.append("B");
+ }
+ str.append("]");
+ }
+
+ str.append(" ");
+ str.append(super.toString());
+ Header hdr = getHeader();
+ if (hdr != null)
+ {
+ for (Struct st : hdr.getStructs())
+ {
+ str.append("\n ");
+ str.append(st);
+ }
+ }
+ ByteBuffer body = getBody();
+ if (body != null)
+ {
+ str.append("\n body=");
+ str.append(str(body, 64));
+ }
+
+ return str.toString();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.java
new file mode 100644
index 0000000000..a90948fc1d
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.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.transport;
+
+
+/**
+ * ProtocolDelegate
+ *
+ */
+
+public interface ProtocolDelegate<C>
+{
+
+ void init(C context, ProtocolHeader header);
+
+ void control(C context, Method control);
+
+ void command(C context, Method command);
+
+ void error(C context, ProtocolError error);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.java
new file mode 100644
index 0000000000..bd6ab81997
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.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.transport;
+
+import org.apache.qpid.transport.network.NetworkDelegate;
+import org.apache.qpid.transport.network.NetworkEvent;
+
+
+/**
+ * ProtocolError
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class ProtocolError implements NetworkEvent, ProtocolEvent
+{
+
+ private int channel;
+ private final byte track;
+ private final String format;
+ private final Object[] args;
+
+ public ProtocolError(byte track, String format, Object ... args)
+ {
+ this.track = track;
+ this.format = format;
+ this.args = args;
+ }
+
+ public int getChannel()
+ {
+ return channel;
+ }
+
+ public void setChannel(int channel)
+ {
+ this.channel = channel;
+ }
+
+ public byte getEncodedTrack()
+ {
+ return track;
+ }
+
+ public String getMessage()
+ {
+ return String.format(format, args);
+ }
+
+ public <C> void delegate(C context, ProtocolDelegate<C> delegate)
+ {
+ delegate.error(context, this);
+ }
+
+ public void delegate(NetworkDelegate delegate)
+ {
+ delegate.error(this);
+ }
+
+ public String toString()
+ {
+ return String.format("protocol error: %s", getMessage());
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.java
new file mode 100644
index 0000000000..60234c1537
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.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.transport;
+
+
+/**
+ * ProtocolEvent
+ *
+ */
+
+public interface ProtocolEvent
+{
+
+ int getChannel();
+
+ void setChannel(int channel);
+
+ byte getEncodedTrack();
+
+ <C> void delegate(C context, ProtocolDelegate<C> delegate);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java
new file mode 100644
index 0000000000..fa0c1e9c63
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.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.transport;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.transport.network.NetworkDelegate;
+import org.apache.qpid.transport.network.NetworkEvent;
+import org.apache.qpid.transport.network.Frame;
+
+
+/**
+ * ProtocolHeader
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class ProtocolHeader implements NetworkEvent, ProtocolEvent
+{
+
+ private static final byte[] AMQP = {'A', 'M', 'Q', 'P' };
+ private static final byte CLASS = 1;
+
+ final private byte instance;
+ final private byte major;
+ final private byte minor;
+ private int channel;
+
+ public ProtocolHeader(byte instance, byte major, byte minor)
+ {
+ this.instance = instance;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public ProtocolHeader(int instance, int major, int minor)
+ {
+ this((byte) instance, (byte) major, (byte) minor);
+ }
+
+ public byte getInstance()
+ {
+ return instance;
+ }
+
+ public byte getMajor()
+ {
+ return major;
+ }
+
+ public byte getMinor()
+ {
+ return minor;
+ }
+
+ public int getChannel()
+ {
+ return channel;
+ }
+
+ public void setChannel(int channel)
+ {
+ this.channel = channel;
+ }
+
+ public byte getEncodedTrack()
+ {
+ return Frame.L1;
+ }
+
+ public ByteBuffer toByteBuffer()
+ {
+ ByteBuffer buf = ByteBuffer.allocate(8);
+ buf.put(AMQP);
+ buf.put(CLASS);
+ buf.put(instance);
+ buf.put(major);
+ buf.put(minor);
+ buf.flip();
+ return buf;
+ }
+
+ public <C> void delegate(C context, ProtocolDelegate<C> delegate)
+ {
+ delegate.init(context, this);
+ }
+
+ public void delegate(NetworkDelegate delegate)
+ {
+ delegate.init(this);
+ }
+
+ public String toString()
+ {
+ return String.format("AMQP.%d %d-%d", instance, major, minor);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java
new file mode 100644
index 0000000000..db8064268c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+
+/**
+ * ProtocolVersionException
+ *
+ */
+
+public final class ProtocolVersionException extends ConnectionException
+{
+
+ private final byte major;
+ private final byte minor;
+
+ public ProtocolVersionException(byte major, byte minor, Throwable cause)
+ {
+ super(String.format("version mismatch: %s-%s", major, minor), cause);
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public ProtocolVersionException(byte major, byte minor)
+ {
+ this(major, minor, null);
+ }
+
+ public byte getMajor()
+ {
+ return this.major;
+ }
+
+ public byte getMinor()
+ {
+ return this.minor;
+ }
+
+ @Override public void rethrow()
+ {
+ throw new ProtocolVersionException(major, minor, this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Range.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Range.java
new file mode 100644
index 0000000000..f4335dc8a6
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Range.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.transport;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.qpid.util.Serial.*;
+
+
+/**
+ * Range
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class Range
+{
+ private final int lower;
+ private final int upper;
+
+ public Range(int lower, int upper)
+ {
+ this.lower = lower;
+ this.upper = upper;
+ }
+
+ public int getLower()
+ {
+ return lower;
+ }
+
+ public int getUpper()
+ {
+ return upper;
+ }
+
+ public boolean includes(int value)
+ {
+ return le(lower, value) && le(value, upper);
+ }
+
+ public boolean includes(Range range)
+ {
+ return includes(range.lower) && includes(range.upper);
+ }
+
+ public boolean intersects(Range range)
+ {
+ return (includes(range.lower) || includes(range.upper) ||
+ range.includes(lower) || range.includes(upper));
+ }
+
+ public boolean touches(Range range)
+ {
+ return (intersects(range) ||
+ includes(range.upper + 1) || includes(range.lower - 1) ||
+ range.includes(upper + 1) || range.includes(lower - 1));
+ }
+
+ public Range span(Range range)
+ {
+ return new Range(min(lower, range.lower), max(upper, range.upper));
+ }
+
+ public List<Range> subtract(Range range)
+ {
+ List<Range> result = new ArrayList<Range>();
+
+ if (includes(range.lower) && le(lower, range.lower - 1))
+ {
+ result.add(new Range(lower, range.lower - 1));
+ }
+
+ if (includes(range.upper) && le(range.upper + 1, upper))
+ {
+ result.add(new Range(range.upper + 1, upper));
+ }
+
+ if (result.isEmpty() && !range.includes(this))
+ {
+ result.add(this);
+ }
+
+ return result;
+ }
+
+ public Range intersect(Range range)
+ {
+ int l = max(lower, range.lower);
+ int r = min(upper, range.upper);
+ if (gt(l, r))
+ {
+ return null;
+ }
+ else
+ {
+ return new Range(l, r);
+ }
+ }
+
+ public String toString()
+ {
+ return "[" + lower + ", " + upper + "]";
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java
new file mode 100644
index 0000000000..9b2744ee8b
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+import static org.apache.qpid.util.Serial.*;
+
+/**
+ * RangeSet
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class RangeSet implements Iterable<Range>
+{
+
+ private LinkedList<Range> ranges = new LinkedList<Range>();
+
+ public int size()
+ {
+ return ranges.size();
+ }
+
+ public Iterator<Range> iterator()
+ {
+ return ranges.iterator();
+ }
+
+ public Range getFirst()
+ {
+ return ranges.getFirst();
+ }
+
+ public boolean includes(Range range)
+ {
+ for (Range r : this)
+ {
+ if (r.includes(range))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean includes(int n)
+ {
+ for (Range r : this)
+ {
+ if (r.includes(n))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void add(Range range)
+ {
+ ListIterator<Range> it = ranges.listIterator();
+
+ while (it.hasNext())
+ {
+ Range next = it.next();
+ if (range.touches(next))
+ {
+ it.remove();
+ range = range.span(next);
+ }
+ else if (lt(range.getUpper(), next.getLower()))
+ {
+ it.previous();
+ it.add(range);
+ return;
+ }
+ }
+
+ it.add(range);
+ }
+
+ public void add(int lower, int upper)
+ {
+ add(new Range(lower, upper));
+ }
+
+ public void add(int value)
+ {
+ add(value, value);
+ }
+
+ public void clear()
+ {
+ ranges.clear();
+ }
+
+ public RangeSet copy()
+ {
+ RangeSet copy = new RangeSet();
+ copy.ranges.addAll(ranges);
+ return copy;
+ }
+
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer();
+ str.append("{");
+ boolean first = true;
+ for (Range range : ranges)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ str.append(", ");
+ }
+ str.append(range);
+ }
+ str.append("}");
+ return str.toString();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Receiver.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Receiver.java
new file mode 100644
index 0000000000..2a994580dc
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Receiver.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.transport;
+
+
+/**
+ * Receiver
+ *
+ */
+
+public interface Receiver<T>
+{
+
+ void received(T msg);
+
+ void exception(Throwable t);
+
+ void closed();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Result.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Result.java
new file mode 100644
index 0000000000..1116492a8d
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Result.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.transport;
+
+
+/**
+ * Result
+ *
+ * @author Rafael H. Schloming
+ */
+
+public abstract class Result extends Struct {}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sender.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sender.java
new file mode 100644
index 0000000000..9a6f675d7f
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sender.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.transport;
+
+
+/**
+ * Sender
+ *
+ */
+
+public interface Sender<T>
+{
+
+ void send(T msg);
+
+ void flush();
+
+ void close();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SenderException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SenderException.java
new file mode 100644
index 0000000000..a96079dc27
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SenderException.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.transport;
+
+
+/**
+ * SenderException
+ *
+ */
+
+public class SenderException extends TransportException
+{
+
+ public SenderException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public SenderException(String message)
+ {
+ super(message);
+ }
+
+ public SenderException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public void rethrow()
+ {
+ throw new SenderException(getMessage(), this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java
new file mode 100644
index 0000000000..529871fbf6
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java
@@ -0,0 +1,154 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.util.Collections;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.qpid.QpidException;
+
+import org.apache.qpid.SecurityHelper;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+
+import static org.apache.qpid.transport.Connection.State.*;
+
+
+/**
+ * ServerDelegate
+ *
+ */
+
+public class ServerDelegate extends ConnectionDelegate
+{
+
+ private SaslServer saslServer;
+
+ public void init(Connection conn, ProtocolHeader hdr)
+ {
+ conn.send(new ProtocolHeader(1, 0, 10));
+ List<Object> utf8 = new ArrayList<Object>();
+ utf8.add("utf8");
+ conn.connectionStart(null, Collections.EMPTY_LIST, utf8);
+ }
+
+ @Override public void connectionStartOk(Connection conn, ConnectionStartOk ok)
+ {
+ conn.setLocale(ok.getLocale());
+ String mechanism = ok.getMechanism();
+
+ if (mechanism == null || mechanism.length() == 0)
+ {
+ conn.connectionTune
+ (Integer.MAX_VALUE,
+ org.apache.qpid.transport.network.ConnectionBinding.MAX_FRAME_SIZE,
+ 0, Integer.MAX_VALUE);
+ return;
+ }
+
+ try
+ {
+ SaslServer ss = Sasl.createSaslServer
+ (mechanism, "AMQP", "localhost", null, null);
+ if (ss == null)
+ {
+ conn.connectionClose
+ (ConnectionCloseCode.CONNECTION_FORCED,
+ "null SASL mechanism: " + mechanism);
+ return;
+ }
+ conn.setSaslServer(ss);
+ secure(conn, ok.getResponse());
+ }
+ catch (SaslException e)
+ {
+ conn.exception(e);
+ }
+ }
+
+ private void secure(Connection conn, byte[] response)
+ {
+ SaslServer ss = conn.getSaslServer();
+ try
+ {
+ byte[] challenge = ss.evaluateResponse(response);
+ if (ss.isComplete())
+ {
+ ss.dispose();
+ conn.connectionTune
+ (Integer.MAX_VALUE,
+ org.apache.qpid.transport.network.ConnectionBinding.MAX_FRAME_SIZE,
+ 0, Integer.MAX_VALUE);
+ }
+ else
+ {
+ conn.connectionSecure(challenge);
+ }
+ }
+ catch (SaslException e)
+ {
+ conn.exception(e);
+ }
+ }
+
+ @Override public void connectionSecureOk(Connection conn, ConnectionSecureOk ok)
+ {
+ secure(conn, ok.getResponse());
+ }
+
+ @Override public void connectionTuneOk(Connection conn, ConnectionTuneOk ok)
+ {
+
+ }
+
+ @Override public void connectionOpen(Connection conn, ConnectionOpen open)
+ {
+ conn.connectionOpenOk(Collections.EMPTY_LIST);
+ conn.setState(OPEN);
+ }
+
+ public Session getSession(Connection conn, SessionAttach atc)
+ {
+ return new Session(conn, new Binary(atc.getName()), 0);
+ }
+
+ @Override public void sessionAttach(Connection conn, SessionAttach atc)
+ {
+ Session ssn = getSession(conn, atc);
+ conn.map(ssn, atc.getChannel());
+ ssn.sessionAttached(atc.getName());
+ ssn.setState(Session.State.OPEN);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java
new file mode 100644
index 0000000000..1a44ed8973
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java
@@ -0,0 +1,799 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+
+import org.apache.qpid.transport.network.Frame;
+
+import org.apache.qpid.transport.util.Logger;
+import org.apache.qpid.transport.util.Waiter;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.apache.qpid.transport.Option.*;
+import static org.apache.qpid.transport.Session.State.*;
+import static org.apache.qpid.transport.util.Functions.*;
+import static org.apache.qpid.util.Serial.*;
+import static org.apache.qpid.util.Strings.*;
+
+/**
+ * Session
+ *
+ * @author Rafael H. Schloming
+ */
+
+public class Session extends SessionInvoker
+{
+
+ private static final Logger log = Logger.get(Session.class);
+
+ enum State { NEW, DETACHED, OPEN, CLOSING, CLOSED }
+
+ class DefaultSessionListener implements SessionListener
+ {
+
+ public void opened(Session ssn) {}
+
+ public void message(Session ssn, MessageTransfer xfr)
+ {
+ log.info("message: %s", xfr);
+ }
+
+ public void exception(Session ssn, SessionException exc)
+ {
+ log.error(exc, "session exception");
+ }
+
+ public void closed(Session ssn) {}
+ }
+
+ public static final int UNLIMITED_CREDIT = 0xFFFFFFFF;
+
+ private Connection connection;
+ private Binary name;
+ private long expiry;
+ private int channel;
+ private SessionDelegate delegate = new SessionDelegate();
+ private SessionListener listener = new DefaultSessionListener();
+ private long timeout = 60000;
+ private boolean autoSync = false;
+
+ private boolean incomingInit;
+ // incoming command count
+ private int commandsIn;
+ // completed incoming commands
+ private final Object processedLock = new Object();
+ private RangeSet processed;
+ private int maxProcessed;
+ private int syncPoint;
+
+ // outgoing command count
+ private int commandsOut = 0;
+ private Method[] commands = new Method[64*1024];
+ private int maxComplete = commandsOut - 1;
+ private boolean needSync = false;
+
+ private State state = NEW;
+
+ Session(Connection connection, Binary name, long expiry)
+ {
+ this.connection = connection;
+ this.name = name;
+ this.expiry = expiry;
+ initReceiver();
+ }
+
+ public Connection getConnection()
+ {
+ return connection;
+ }
+
+ public Binary getName()
+ {
+ return name;
+ }
+
+ void setExpiry(long expiry)
+ {
+ this.expiry = expiry;
+ }
+
+ int getChannel()
+ {
+ return channel;
+ }
+
+ void setChannel(int channel)
+ {
+ this.channel = channel;
+ }
+
+ public void setSessionListener(SessionListener listener)
+ {
+ if (listener == null)
+ {
+ this.listener = new DefaultSessionListener();
+ }
+ else
+ {
+ this.listener = listener;
+ }
+ }
+
+ public SessionListener getSessionListener()
+ {
+ return listener;
+ }
+
+ public void setAutoSync(boolean value)
+ {
+ synchronized (commands)
+ {
+ this.autoSync = value;
+ }
+ }
+
+ void setState(State state)
+ {
+ synchronized (commands)
+ {
+ this.state = state;
+ commands.notifyAll();
+ }
+ }
+
+ private void initReceiver()
+ {
+ synchronized (processedLock)
+ {
+ incomingInit = false;
+ processed = new RangeSet();
+ }
+ }
+
+ void attach()
+ {
+ initReceiver();
+ sessionAttach(name.getBytes());
+ // XXX: when the broker and client support full session
+ // recovery we should use expiry as the requested timeout
+ sessionRequestTimeout(0);
+ }
+
+ void resume()
+ {
+ synchronized (commands)
+ {
+ for (int i = maxComplete + 1; lt(i, commandsOut); i++)
+ {
+ Method m = commands[mod(i, commands.length)];
+ if (m != null)
+ {
+ sessionCommandPoint(m.getId(), 0);
+ send(m);
+ }
+ }
+
+ sessionCommandPoint(commandsOut, 0);
+ sessionFlush(COMPLETED);
+ }
+ }
+
+ void dump()
+ {
+ synchronized (commands)
+ {
+ for (Method m : commands)
+ {
+ if (m != null)
+ {
+ System.out.println(m);
+ }
+ }
+ }
+ }
+
+ final void commandPoint(int id)
+ {
+ synchronized (processedLock)
+ {
+ this.commandsIn = id;
+ if (!incomingInit)
+ {
+ incomingInit = true;
+ maxProcessed = commandsIn - 1;
+ syncPoint = maxProcessed;
+ }
+ }
+ }
+
+ public int getCommandsOut()
+ {
+ return commandsOut;
+ }
+
+ public int getCommandsIn()
+ {
+ return commandsIn;
+ }
+
+ public int nextCommandId()
+ {
+ return commandsIn++;
+ }
+
+ final void identify(Method cmd)
+ {
+ if (!incomingInit)
+ {
+ throw new IllegalStateException();
+ }
+
+ int id = nextCommandId();
+ cmd.setId(id);
+
+ if(log.isDebugEnabled())
+ {
+ log.debug("ID: [%s] %s", this.channel, id);
+ }
+
+ //if ((id % 65536) == 0)
+ if ((id & 0xff) == 0)
+ {
+ flushProcessed(TIMELY_REPLY);
+ }
+ }
+
+ public void processed(Method command)
+ {
+ processed(command.getId());
+ }
+
+ public void processed(int command)
+ {
+ processed(new Range(command, command));
+ }
+
+ public void processed(int lower, int upper)
+ {
+
+ processed(new Range(lower, upper));
+ }
+
+ public void processed(Range range)
+ {
+ log.debug("%s processed(%s) %s %s", this, range, syncPoint, maxProcessed);
+
+ boolean flush;
+ synchronized (processedLock)
+ {
+ log.debug("%s", processed);
+ processed.add(range);
+ Range first = processed.getFirst();
+ int lower = first.getLower();
+ int upper = first.getUpper();
+ int old = maxProcessed;
+ if (le(lower, maxProcessed + 1))
+ {
+ maxProcessed = max(maxProcessed, upper);
+ }
+ boolean synced = ge(maxProcessed, syncPoint);
+ flush = lt(old, syncPoint) && synced;
+ if (synced)
+ {
+ syncPoint = maxProcessed;
+ }
+ }
+ if (flush)
+ {
+ flushProcessed();
+ }
+ }
+
+ void flushExpected()
+ {
+ RangeSet rs = new RangeSet();
+ synchronized (processedLock)
+ {
+ if (incomingInit)
+ {
+ rs.add(commandsIn);
+ }
+ }
+ sessionExpected(rs, null);
+ }
+
+ public void flushProcessed(Option ... options)
+ {
+ RangeSet copy;
+ synchronized (processedLock)
+ {
+ copy = processed.copy();
+ }
+ sessionCompleted(copy, options);
+ }
+
+ void knownComplete(RangeSet kc)
+ {
+ synchronized (processedLock)
+ {
+ RangeSet newProcessed = new RangeSet();
+ for (Range pr : processed)
+ {
+ for (Range kr : kc)
+ {
+ for (Range r : pr.subtract(kr))
+ {
+ newProcessed.add(r);
+ }
+ }
+ }
+ this.processed = newProcessed;
+ }
+ }
+
+ void syncPoint()
+ {
+ int id = getCommandsIn() - 1;
+ log.debug("%s synced to %d", this, id);
+ boolean flush;
+ synchronized (processedLock)
+ {
+ syncPoint = id;
+ flush = ge(maxProcessed, syncPoint);
+ }
+ if (flush)
+ {
+ flushProcessed();
+ }
+ }
+
+ boolean complete(int lower, int upper)
+ {
+ //avoid autoboxing
+ if(log.isDebugEnabled())
+ {
+ log.debug("%s complete(%d, %d)", this, lower, upper);
+ }
+ synchronized (commands)
+ {
+ int old = maxComplete;
+ for (int id = max(maxComplete, lower); le(id, upper); id++)
+ {
+ commands[mod(id, commands.length)] = null;
+ }
+ if (le(lower, maxComplete + 1))
+ {
+ maxComplete = max(maxComplete, upper);
+ }
+ log.debug("%s commands remaining: %s", this, commandsOut - maxComplete);
+ commands.notifyAll();
+ return gt(maxComplete, old);
+ }
+ }
+
+ void received(Method m)
+ {
+ m.delegate(this, delegate);
+ }
+
+ private void send(Method m)
+ {
+ m.setChannel(channel);
+ connection.send(m);
+
+ if (!m.isBatch())
+ {
+ connection.flush();
+ }
+ }
+
+ final private boolean isFull(int id)
+ {
+ return id - maxComplete >= commands.length;
+ }
+
+ public void invoke(Method m)
+ {
+ if (m.getEncodedTrack() == Frame.L4)
+ {
+ synchronized (commands)
+ {
+ if (state != OPEN && state != CLOSED)
+ {
+ Waiter w = new Waiter(commands, timeout);
+ while (w.hasTime() && (state != OPEN && state != CLOSED))
+ {
+ w.await();
+ }
+ }
+
+ switch (state)
+ {
+ case OPEN:
+ break;
+ case CLOSED:
+ throw new SessionClosedException();
+ default:
+ throw new SessionException
+ (String.format
+ ("timed out waiting for session to become open " +
+ "(state=%s)", state));
+ }
+
+ int next = commandsOut++;
+ m.setId(next);
+
+ if (isFull(next))
+ {
+ Waiter w = new Waiter(commands, timeout);
+ while (w.hasTime() && isFull(next))
+ {
+ if (state == OPEN)
+ {
+ try
+ {
+ sessionFlush(COMPLETED);
+ }
+ catch (SenderException e)
+ {
+ if (expiry > 0)
+ {
+ // if expiry is > 0 then this will
+ // happen again on resume
+ log.error(e, "error sending flush (full replay buffer)");
+ }
+ else
+ {
+ e.rethrow();
+ }
+ }
+ }
+ w.await();
+ }
+ }
+
+ if (isFull(next))
+ {
+ throw new SessionException("timed out waiting for completion");
+ }
+
+ if (next == 0)
+ {
+ sessionCommandPoint(0, 0);
+ }
+ if (expiry > 0)
+ {
+ commands[mod(next, commands.length)] = m;
+ }
+ if (autoSync)
+ {
+ m.setSync(true);
+ }
+ needSync = !m.isSync();
+ try
+ {
+ send(m);
+ }
+ catch (SenderException e)
+ {
+ if (expiry > 0)
+ {
+ // if expiry is > 0 then this will happen
+ // again on resume
+ log.error(e, "error sending command");
+ }
+ else
+ {
+ e.rethrow();
+ }
+ }
+ if (autoSync)
+ {
+ sync();
+ }
+
+ // flush every 64K commands to avoid ambiguity on
+ // wraparound
+ if ((next % 65536) == 0)
+ {
+ try
+ {
+ sessionFlush(COMPLETED);
+ }
+ catch (SenderException e)
+ {
+ if (expiry > 0)
+ {
+ // if expiry is > 0 then this will happen
+ // again on resume
+ log.error(e, "error sending flush (periodic)");
+ }
+ else
+ {
+ e.rethrow();
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ send(m);
+ }
+ }
+
+ public void sync()
+ {
+ sync(timeout);
+ }
+
+ public void sync(long timeout)
+ {
+ log.debug("%s sync()", this);
+ synchronized (commands)
+ {
+ int point = commandsOut - 1;
+
+ if (needSync && lt(maxComplete, point))
+ {
+ executionSync(SYNC);
+ }
+
+ Waiter w = new Waiter(commands, timeout);
+ while (w.hasTime() && state != CLOSED && lt(maxComplete, point))
+ {
+ log.debug("%s waiting for[%d]: %d, %s", this, point,
+ maxComplete, commands);
+ w.await();
+ }
+
+ if (lt(maxComplete, point))
+ {
+ if (state == CLOSED)
+ {
+ throw new SessionException(getException());
+ }
+ else
+ {
+ throw new SessionException
+ (String.format
+ ("timed out waiting for sync: complete = %s, point = %s", maxComplete, point));
+ }
+ }
+ }
+ }
+
+ private Map<Integer,ResultFuture<?>> results =
+ new HashMap<Integer,ResultFuture<?>>();
+ private ExecutionException exception = null;
+
+ void result(int command, Struct result)
+ {
+ ResultFuture<?> future;
+ synchronized (results)
+ {
+ future = results.remove(command);
+ }
+ future.set(result);
+ }
+
+ void setException(ExecutionException exc)
+ {
+ synchronized (results)
+ {
+ if (exception != null)
+ {
+ throw new IllegalStateException
+ (String.format
+ ("too many exceptions: %s, %s", exception, exc));
+ }
+ exception = exc;
+ }
+ }
+
+ private ConnectionClose close = null;
+
+ void closeCode(ConnectionClose close)
+ {
+ this.close = close;
+ }
+
+ ExecutionException getException()
+ {
+ synchronized (results)
+ {
+ return exception;
+ }
+ }
+
+ protected <T> Future<T> invoke(Method m, Class<T> klass)
+ {
+ synchronized (commands)
+ {
+ int command = commandsOut;
+ ResultFuture<T> future = new ResultFuture<T>(klass);
+ synchronized (results)
+ {
+ results.put(command, future);
+ }
+ invoke(m);
+ return future;
+ }
+ }
+
+ private class ResultFuture<T> implements Future<T>
+ {
+
+ private final Class<T> klass;
+ private T result;
+
+ private ResultFuture(Class<T> klass)
+ {
+ this.klass = klass;
+ }
+
+ private void set(Struct result)
+ {
+ synchronized (this)
+ {
+ this.result = klass.cast(result);
+ notifyAll();
+ }
+ }
+
+ public T get(long timeout)
+ {
+ synchronized (this)
+ {
+ Waiter w = new Waiter(this, timeout);
+ while (w.hasTime() && state != CLOSED && !isDone())
+ {
+ log.debug("%s waiting for result: %s", Session.this, this);
+ w.await();
+ }
+ }
+
+ if (isDone())
+ {
+ return result;
+ }
+ else if (state == CLOSED)
+ {
+ throw new SessionException(getException());
+ }
+ else
+ {
+ throw new SessionException
+ (String.format("%s timed out waiting for result: %s",
+ Session.this, this));
+ }
+ }
+
+ public T get()
+ {
+ return get(timeout);
+ }
+
+ public boolean isDone()
+ {
+ return result != null;
+ }
+
+ public String toString()
+ {
+ return String.format("Future(%s)", isDone() ? result : klass);
+ }
+
+ }
+
+ public final void messageTransfer(String destination,
+ MessageAcceptMode acceptMode,
+ MessageAcquireMode acquireMode,
+ Header header,
+ byte[] body,
+ Option ... _options) {
+ messageTransfer(destination, acceptMode, acquireMode, header,
+ ByteBuffer.wrap(body), _options);
+ }
+
+ public final void messageTransfer(String destination,
+ MessageAcceptMode acceptMode,
+ MessageAcquireMode acquireMode,
+ Header header,
+ String body,
+ Option ... _options) {
+ messageTransfer(destination, acceptMode, acquireMode, header,
+ toUTF8(body), _options);
+ }
+
+ public void close()
+ {
+ synchronized (commands)
+ {
+ state = CLOSING;
+ // XXX: we manually set the expiry to zero here to
+ // simulate full session recovery in brokers that don't
+ // support it, we should remove this line when there is
+ // broker support for full session resume:
+ expiry = 0;
+ sessionRequestTimeout(0);
+ sessionDetach(name.getBytes());
+ Waiter w = new Waiter(commands, timeout);
+ while (w.hasTime() && state != CLOSED)
+ {
+ w.await();
+ }
+
+ if (state != CLOSED)
+ {
+ throw new SessionException("close() timed out");
+ }
+
+ connection.removeSession(this);
+ }
+ }
+
+ public void exception(Throwable t)
+ {
+ log.error(t, "caught exception");
+ }
+
+ public void closed()
+ {
+ synchronized (commands)
+ {
+ if (expiry == 0)
+ {
+ state = CLOSED;
+ }
+ else
+ {
+ state = DETACHED;
+ }
+
+ commands.notifyAll();
+
+ synchronized (results)
+ {
+ for (ResultFuture<?> result : results.values())
+ {
+ synchronized(result)
+ {
+ result.notifyAll();
+ }
+ }
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return String.format("ssn:%s", name);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java
new file mode 100644
index 0000000000..64f9039484
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java
@@ -0,0 +1,49 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.util.Collections;
+
+
+/**
+ * SessionClosedException
+ *
+ */
+
+public class SessionClosedException extends SessionException
+{
+
+ public SessionClosedException()
+ {
+ this(null);
+ }
+
+ public SessionClosedException(Throwable cause)
+ {
+ super("session closed", null, cause);
+ }
+
+ @Override public void rethrow()
+ {
+ throw new SessionClosedException(this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java
new file mode 100644
index 0000000000..b2c22f22e9
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import org.apache.qpid.transport.util.Logger;
+
+
+/**
+ * SessionDelegate
+ *
+ * @author Rafael H. Schloming
+ */
+
+public class SessionDelegate
+ extends MethodDelegate<Session>
+ implements ProtocolDelegate<Session>
+{
+ private static final Logger log = Logger.get(SessionDelegate.class);
+
+ public void init(Session ssn, ProtocolHeader hdr) { }
+
+ public void control(Session ssn, Method method) {
+ method.dispatch(ssn, this);
+ }
+
+ public void command(Session ssn, Method method) {
+ ssn.identify(method);
+ method.dispatch(ssn, this);
+ if (!method.hasPayload())
+ {
+ ssn.processed(method);
+ }
+ }
+
+ public void error(Session ssn, ProtocolError error) { }
+
+ public void handle(Session ssn, Method method)
+ {
+ log.warn("UNHANDLED: [%s] %s", ssn, method);
+ }
+
+ @Override public void sessionAttached(Session ssn, SessionAttached atc)
+ {
+ ssn.setState(Session.State.OPEN);
+ }
+
+ @Override public void sessionTimeout(Session ssn, SessionTimeout t)
+ {
+ // XXX: we ignore this right now, we should uncomment this
+ // when full session resume is supported:
+ // ssn.setExpiry(t.getTimeout());
+ }
+
+ @Override public void sessionCompleted(Session ssn, SessionCompleted cmp)
+ {
+ RangeSet ranges = cmp.getCommands();
+ RangeSet known = null;
+ if (cmp.getTimelyReply())
+ {
+ known = new RangeSet();
+ }
+
+ if (ranges != null)
+ {
+ for (Range range : ranges)
+ {
+ boolean advanced = ssn.complete(range.getLower(), range.getUpper());
+ if (advanced && known != null)
+ {
+ known.add(range);
+ }
+ }
+ }
+
+ if (known != null)
+ {
+ ssn.sessionKnownCompleted(known);
+ }
+ }
+
+ @Override public void sessionKnownCompleted(Session ssn, SessionKnownCompleted kcmp)
+ {
+ RangeSet kc = kcmp.getCommands();
+ if (kc != null)
+ {
+ ssn.knownComplete(kc);
+ }
+ }
+
+ @Override public void sessionFlush(Session ssn, SessionFlush flush)
+ {
+ if (flush.getCompleted())
+ {
+ ssn.flushProcessed();
+ }
+ if (flush.getConfirmed())
+ {
+ ssn.flushProcessed();
+ }
+ if (flush.getExpected())
+ {
+ ssn.flushExpected();
+ }
+ }
+
+ @Override public void sessionCommandPoint(Session ssn, SessionCommandPoint scp)
+ {
+ ssn.commandPoint(scp.getCommandId());
+ }
+
+ @Override public void executionSync(Session ssn, ExecutionSync sync)
+ {
+ ssn.syncPoint();
+ }
+
+ @Override public void executionResult(Session ssn, ExecutionResult result)
+ {
+ ssn.result(result.getCommandId(), result.getValue());
+ }
+
+ @Override public void executionException(Session ssn, ExecutionException exc)
+ {
+ ssn.setException(exc);
+ }
+
+ @Override public void messageTransfer(Session ssn, MessageTransfer xfr)
+ {
+ ssn.getSessionListener().message(ssn, xfr);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionException.java
new file mode 100644
index 0000000000..c4fc9558a1
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionException.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.transport;
+
+import java.util.List;
+
+/**
+ * SessionException
+ *
+ */
+
+public class SessionException extends TransportException
+{
+
+ private ExecutionException exception;
+
+ public SessionException(String message, ExecutionException exception, Throwable cause)
+ {
+ super(message, cause);
+ this.exception = exception;
+ }
+
+ public SessionException(ExecutionException exception)
+ {
+ this(String.valueOf(exception), exception, null);
+ }
+
+ public SessionException(String message)
+ {
+ this(message, null, null);
+ }
+
+ public ExecutionException getException()
+ {
+ return exception;
+ }
+
+ @Override public void rethrow()
+ {
+ throw new SessionException(getMessage(), exception, this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java
new file mode 100644
index 0000000000..63690177f9
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionListener.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.transport;
+
+
+/**
+ * SessionListener
+ *
+ */
+
+public interface SessionListener
+{
+
+ void opened(Session session);
+
+ void message(Session ssn, MessageTransfer xfr);
+
+ void exception(Session session, SessionException exception);
+
+ void closed(Session session);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sink.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sink.java
new file mode 100644
index 0000000000..622993effb
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Sink.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.transport.network.ConnectionBinding;
+import org.apache.qpid.transport.network.io.IoAcceptor;
+
+/**
+ * Sink
+ *
+ */
+
+public class Sink implements SessionListener
+{
+
+ private static final String FORMAT_HDR = "%-12s %-18s %-18s %-18s";
+ private static final String FORMAT_ROW = "SSN#%-8X %-18s %-18s %-18s";
+
+ private long interval = 100000;
+ private long start = System.currentTimeMillis();
+ private long count = 0;
+ private long bytes = 0;
+ private long interval_start = start;
+ private long bytes_start = bytes;
+ private long time = start;
+ private int id = System.identityHashCode(this);
+
+ public Sink()
+ {
+ }
+
+ private double msg_rate()
+ {
+ return 1000 * (double) count / (double) (time - start);
+ }
+
+ private double byte_rate()
+ {
+ return (1000 * (double) bytes / (double) (time - start)) / (1024*1024);
+ }
+
+ private double msg_interval_rate()
+ {
+ return 1000 * (double) interval / (double) (time - interval_start);
+ }
+
+ private double byte_interval_rate()
+ {
+ return (1000 * (double) (bytes - bytes_start) / (double) (time - interval_start)) / (1024*1024);
+ }
+
+ private String rates()
+ {
+ return String.format("%.2f/%.2f", msg_rate(), byte_rate());
+ }
+
+ private String interval_rates()
+ {
+ return String.format("%.2f/%.2f", msg_interval_rate(), byte_interval_rate());
+ }
+
+ private String counts()
+ {
+ return String.format("%d/%.2f", count, ((double) bytes)/(1024*1024));
+ }
+
+ public void opened(Session ssn) {}
+
+ public void message(Session ssn, MessageTransfer xfr)
+ {
+ count++;
+ bytes += xfr.getBody().remaining();
+ if ((count % interval) == 0)
+ {
+ time = System.currentTimeMillis();
+ System.out.println
+ (String.format
+ (FORMAT_ROW, id, counts(), rates(), interval_rates()));
+ interval_start = time;
+ bytes_start = bytes;
+ }
+ ssn.processed(xfr);
+ }
+
+ public void exception(Session ssn, SessionException exc)
+ {
+ exc.printStackTrace();
+ }
+
+ public void closed(Session ssn) {}
+
+ public static final void main(String[] args) throws IOException
+ {
+ ConnectionDelegate delegate = new ServerDelegate()
+ {
+ @Override public Session getSession(Connection conn, SessionAttach atc)
+ {
+ Session ssn = super.getSession(conn, atc);
+ ssn.setSessionListener(new Sink());
+ return ssn;
+ }
+ };
+
+ IoAcceptor ioa = new IoAcceptor
+ ("0.0.0.0", 5672, ConnectionBinding.get(delegate));
+ System.out.println
+ (String.format
+ (FORMAT_HDR, "Session", "Count/MBytes", "Cumulative Rate", "Interval Rate"));
+ System.out.println
+ (String.format
+ (FORMAT_HDR, "-------", "------------", "---------------", "-------------"));
+ ioa.start();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Struct.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Struct.java
new file mode 100644
index 0000000000..22bd9f34ad
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/Struct.java
@@ -0,0 +1,142 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.transport.codec.Decoder;
+import org.apache.qpid.transport.codec.Encodable;
+import org.apache.qpid.transport.codec.Encoder;
+
+
+/**
+ * Struct
+ *
+ * @author Rafael H. Schloming
+ */
+
+public abstract class Struct implements Encodable
+{
+
+ public static Struct create(int type)
+ {
+ return StructFactory.create(type);
+ }
+
+ boolean dirty = true;
+
+ public boolean isDirty()
+ {
+ return dirty;
+ }
+
+ public void setDirty(boolean dirty)
+ {
+ this.dirty = dirty;
+ }
+
+ public abstract int getStructType();
+
+ public abstract int getSizeWidth();
+
+ public abstract int getPackWidth();
+
+ public final int getEncodedType()
+ {
+ int type = getStructType();
+ if (type < 0)
+ {
+ throw new UnsupportedOperationException();
+ }
+ return type;
+ }
+
+ private final boolean isBit(Field<?,?> f)
+ {
+ return f.getType().equals(Boolean.class);
+ }
+
+ private final boolean packed()
+ {
+ return getPackWidth() > 0;
+ }
+
+ private final boolean encoded(Field<?,?> f)
+ {
+ return !packed() || !isBit(f) && f.has(this);
+ }
+
+ private final int getFlagWidth()
+ {
+ return (getFields().size() + 7)/8;
+ }
+
+ private final int getPaddWidth()
+ {
+ int pw = getPackWidth() - getFlagWidth();
+ assert pw > 0;
+ return pw;
+ }
+
+ private final int getFlagCount()
+ {
+ return 8*getPackWidth();
+ }
+
+ private final int getReservedFlagCount()
+ {
+ return getFlagCount() - getFields().size();
+ }
+
+ public abstract void read(Decoder dec);
+
+ public abstract void write(Encoder enc);
+
+ public abstract Map<String,Object> getFields();
+
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+ str.append(getClass().getSimpleName());
+
+ str.append("(");
+ boolean first = true;
+ for (Map.Entry<String,Object> me : getFields().entrySet())
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ str.append(", ");
+ }
+ str.append(me.getKey());
+ str.append("=");
+ str.append(me.getValue());
+ }
+ str.append(")");
+
+ return str.toString();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportException.java
new file mode 100644
index 0000000000..0de190dfad
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportException.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+
+/**
+ * TransportException
+ */
+
+public class TransportException extends RuntimeException
+{
+
+ public TransportException(String msg)
+ {
+ super(msg);
+ }
+
+ public TransportException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+
+ public TransportException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public void rethrow()
+ {
+ throw new TransportException(getMessage(), this);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java
new file mode 100644
index 0000000000..a8a4997ae7
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java
@@ -0,0 +1,470 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.io.UnsupportedEncodingException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.transport.Binary;
+import org.apache.qpid.transport.RangeSet;
+import org.apache.qpid.transport.Struct;
+import org.apache.qpid.transport.Type;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+
+/**
+ * AbstractDecoder
+ *
+ * @author Rafael H. Schloming
+ */
+
+abstract class AbstractDecoder implements Decoder
+{
+
+ private final Map<Binary,String> str8cache = new LinkedHashMap<Binary,String>()
+ {
+ @Override protected boolean removeEldestEntry(Map.Entry<Binary,String> me)
+ {
+ return size() > 4*1024;
+ }
+ };
+
+ protected abstract byte doGet();
+
+ protected abstract void doGet(byte[] bytes);
+
+ protected byte get()
+ {
+ return doGet();
+ }
+
+ protected void get(byte[] bytes)
+ {
+ doGet(bytes);
+ }
+
+ protected Binary get(int size)
+ {
+ byte[] bytes = new byte[size];
+ get(bytes);
+ return new Binary(bytes);
+ }
+
+ protected short uget()
+ {
+ return (short) (0xFF & get());
+ }
+
+ public short readUint8()
+ {
+ return uget();
+ }
+
+ public int readUint16()
+ {
+ int i = uget() << 8;
+ i |= uget();
+ return i;
+ }
+
+ public long readUint32()
+ {
+ long l = uget() << 24;
+ l |= uget() << 16;
+ l |= uget() << 8;
+ l |= uget();
+ return l;
+ }
+
+ public int readSequenceNo()
+ {
+ return (int) readUint32();
+ }
+
+ public long readUint64()
+ {
+ long l = 0;
+ for (int i = 0; i < 8; i++)
+ {
+ l |= ((long) (0xFF & get())) << (56 - i*8);
+ }
+ return l;
+ }
+
+ public long readDatetime()
+ {
+ return readUint64();
+ }
+
+ private static final String decode(byte[] bytes, int offset, int length, String charset)
+ {
+ try
+ {
+ return new String(bytes, offset, length, charset);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final String decode(byte[] bytes, String charset)
+ {
+ return decode(bytes, 0, bytes.length, charset);
+ }
+
+ public String readStr8()
+ {
+ short size = readUint8();
+ Binary bin = get(size);
+ String str = str8cache.get(bin);
+ if (str == null)
+ {
+ str = decode(bin.array(), bin.offset(), bin.size(), "UTF-8");
+ str8cache.put(bin, str);
+ }
+ return str;
+ }
+
+ public String readStr16()
+ {
+ int size = readUint16();
+ byte[] bytes = new byte[size];
+ get(bytes);
+ return decode(bytes, "UTF-8");
+ }
+
+ public byte[] readVbin8()
+ {
+ int size = readUint8();
+ byte[] bytes = new byte[size];
+ get(bytes);
+ return bytes;
+ }
+
+ public byte[] readVbin16()
+ {
+ int size = readUint16();
+ byte[] bytes = new byte[size];
+ get(bytes);
+ return bytes;
+ }
+
+ public byte[] readVbin32()
+ {
+ int size = (int) readUint32();
+ byte[] bytes = new byte[size];
+ get(bytes);
+ return bytes;
+ }
+
+ public RangeSet readSequenceSet()
+ {
+ int count = readUint16()/8;
+ if (count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ RangeSet ranges = new RangeSet();
+ for (int i = 0; i < count; i++)
+ {
+ ranges.add(readSequenceNo(), readSequenceNo());
+ }
+ return ranges;
+ }
+ }
+
+ public RangeSet readByteRanges()
+ {
+ throw new Error("not implemented");
+ }
+
+ public UUID readUuid()
+ {
+ long msb = readUint64();
+ long lsb = readUint64();
+ return new UUID(msb, lsb);
+ }
+
+ public String readContent()
+ {
+ throw new Error("Deprecated");
+ }
+
+ public Struct readStruct(int type)
+ {
+ Struct st = Struct.create(type);
+ int width = st.getSizeWidth();
+ if (width > 0)
+ {
+ long size = readSize(width);
+ if (size == 0)
+ {
+ return null;
+ }
+ }
+ if (type > 0)
+ {
+ int code = readUint16();
+ assert code == type;
+ }
+ st.read(this);
+ return st;
+ }
+
+ public Struct readStruct32()
+ {
+ long size = readUint32();
+ if (size == 0)
+ {
+ return null;
+ }
+ else
+ {
+ int type = readUint16();
+ Struct result = Struct.create(type);
+ result.read(this);
+ return result;
+ }
+ }
+
+ public Map<String,Object> readMap()
+ {
+ long size = readUint32();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ long count = readUint32();
+
+ if (count == 0)
+ {
+ return Collections.EMPTY_MAP;
+ }
+
+ Map<String,Object> result = new LinkedHashMap();
+ for (int i = 0; i < count; i++)
+ {
+ String key = readStr8();
+ byte code = get();
+ Type t = getType(code);
+ Object value = read(t);
+ result.put(key, value);
+ }
+
+ return result;
+ }
+
+ public List<Object> readList()
+ {
+ long size = readUint32();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ long count = readUint32();
+
+ if (count == 0)
+ {
+ return Collections.EMPTY_LIST;
+ }
+
+ List<Object> result = new ArrayList();
+ for (int i = 0; i < count; i++)
+ {
+ byte code = get();
+ Type t = getType(code);
+ Object value = read(t);
+ result.add(value);
+ }
+ return result;
+ }
+
+ public List<Object> readArray()
+ {
+ long size = readUint32();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ byte code = get();
+ Type t = getType(code);
+ long count = readUint32();
+
+ if (count == 0)
+ {
+ return Collections.EMPTY_LIST;
+ }
+
+ List<Object> result = new ArrayList<Object>();
+ for (int i = 0; i < count; i++)
+ {
+ Object value = read(t);
+ result.add(value);
+ }
+ return result;
+ }
+
+ private Type getType(byte code)
+ {
+ Type type = Type.get(code);
+ if (type == null)
+ {
+ throw new IllegalArgumentException("unknown code: " + code);
+ }
+ else
+ {
+ return type;
+ }
+ }
+
+ private long readSize(Type t)
+ {
+ if (t.fixed)
+ {
+ return t.width;
+ }
+ else
+ {
+ return readSize(t.width);
+ }
+ }
+
+ private long readSize(int width)
+ {
+ switch (width)
+ {
+ case 1:
+ return readUint8();
+ case 2:
+ return readUint16();
+ case 4:
+ return readUint32();
+ default:
+ throw new IllegalStateException("illegal width: " + width);
+ }
+ }
+
+ private byte[] readBytes(Type t)
+ {
+ long size = readSize(t);
+ byte[] result = new byte[(int) size];
+ get(result);
+ return result;
+ }
+
+ private Object read(Type t)
+ {
+ switch (t)
+ {
+ case BIN8:
+ case UINT8:
+ return readUint8();
+ case INT8:
+ return get();
+ case CHAR:
+ return (char) get();
+ case BOOLEAN:
+ return get() > 0;
+
+ case BIN16:
+ case UINT16:
+ return readUint16();
+
+ case INT16:
+ return (short) readUint16();
+
+ case BIN32:
+ case UINT32:
+ return readUint32();
+
+ case CHAR_UTF32:
+ case INT32:
+ return (int) readUint32();
+
+ case FLOAT:
+ return Float.intBitsToFloat((int) readUint32());
+
+ case BIN64:
+ case UINT64:
+ case INT64:
+ case DATETIME:
+ return readUint64();
+
+ case DOUBLE:
+ return Double.longBitsToDouble(readUint64());
+
+ case UUID:
+ return readUuid();
+
+ case STR8:
+ return readStr8();
+
+ case STR16:
+ return readStr16();
+
+ case STR8_LATIN:
+ case STR8_UTF16:
+ case STR16_LATIN:
+ case STR16_UTF16:
+ // XXX: need to do character conversion
+ return new String(readBytes(t));
+
+ case MAP:
+ return readMap();
+ case LIST:
+ return readList();
+ case ARRAY:
+ return readArray();
+ case STRUCT32:
+ return readStruct32();
+
+ case BIN40:
+ case DEC32:
+ case BIN72:
+ case DEC64:
+ // XXX: what types are we supposed to use here?
+ return readBytes(t);
+
+ case VOID:
+ return null;
+
+ default:
+ return readBytes(t);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java
new file mode 100644
index 0000000000..908d14a307
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java
@@ -0,0 +1,620 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.io.UnsupportedEncodingException;
+
+import java.nio.ByteBuffer;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.transport.Range;
+import org.apache.qpid.transport.RangeSet;
+import org.apache.qpid.transport.Struct;
+import org.apache.qpid.transport.Type;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+
+/**
+ * AbstractEncoder
+ *
+ * @author Rafael H. Schloming
+ */
+
+abstract class AbstractEncoder implements Encoder
+{
+
+ private static Map<Class<?>,Type> ENCODINGS = new HashMap<Class<?>,Type>();
+ static
+ {
+ ENCODINGS.put(Boolean.class, Type.BOOLEAN);
+ ENCODINGS.put(String.class, Type.STR16);
+ ENCODINGS.put(Long.class, Type.INT64);
+ ENCODINGS.put(Integer.class, Type.INT32);
+ ENCODINGS.put(Short.class, Type.INT16);
+ ENCODINGS.put(Byte.class, Type.INT8);
+ ENCODINGS.put(Map.class, Type.MAP);
+ ENCODINGS.put(List.class, Type.LIST);
+ ENCODINGS.put(Float.class, Type.FLOAT);
+ ENCODINGS.put(Double.class, Type.DOUBLE);
+ ENCODINGS.put(Character.class, Type.CHAR);
+ ENCODINGS.put(byte[].class, Type.VBIN32);
+ }
+
+ private final Map<String,byte[]> str8cache = new LinkedHashMap<String,byte[]>()
+ {
+ @Override protected boolean removeEldestEntry(Map.Entry<String,byte[]> me)
+ {
+ return size() > 4*1024;
+ }
+ };
+
+ protected abstract void doPut(byte b);
+
+ protected abstract void doPut(ByteBuffer src);
+
+ protected void put(byte b)
+ {
+ doPut(b);
+ }
+
+ protected void put(ByteBuffer src)
+ {
+ doPut(src);
+ }
+
+ protected void put(byte[] bytes)
+ {
+ put(ByteBuffer.wrap(bytes));
+ }
+
+ protected abstract int beginSize8();
+ protected abstract void endSize8(int pos);
+
+ protected abstract int beginSize16();
+ protected abstract void endSize16(int pos);
+
+ protected abstract int beginSize32();
+ protected abstract void endSize32(int pos);
+
+ public void writeUint8(short b)
+ {
+ assert b < 0x100;
+
+ put((byte) b);
+ }
+
+ public void writeUint16(int s)
+ {
+ assert s < 0x10000;
+
+ put(lsb(s >>> 8));
+ put(lsb(s));
+ }
+
+ public void writeUint32(long i)
+ {
+ assert i < 0x100000000L;
+
+ put(lsb(i >>> 24));
+ put(lsb(i >>> 16));
+ put(lsb(i >>> 8));
+ put(lsb(i));
+ }
+
+ public void writeSequenceNo(int i)
+ {
+ writeUint32(i);
+ }
+
+ public void writeUint64(long l)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ put(lsb(l >> (56 - i*8)));
+ }
+ }
+
+
+ public void writeDatetime(long l)
+ {
+ writeUint64(l);
+ }
+
+ private static final byte[] encode(String s, String charset)
+ {
+ try
+ {
+ return s.getBytes(charset);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void writeStr8(String s)
+ {
+ if (s == null)
+ {
+ s = "";
+ }
+
+ byte[] bytes = str8cache.get(s);
+ if (bytes == null)
+ {
+ bytes = encode(s, "UTF-8");
+ str8cache.put(s, bytes);
+ }
+ writeUint8((short) bytes.length);
+ put(bytes);
+ }
+
+ public void writeStr16(String s)
+ {
+ if (s == null)
+ {
+ s = "";
+ }
+
+ byte[] bytes = encode(s, "UTF-8");
+ writeUint16(bytes.length);
+ put(bytes);
+ }
+
+ public void writeVbin8(byte[] bytes)
+ {
+ if (bytes == null) { bytes = new byte[0]; }
+ if (bytes.length > 255)
+ {
+ throw new IllegalArgumentException("array too long: " + bytes.length);
+ }
+ writeUint8((short) bytes.length);
+ put(ByteBuffer.wrap(bytes));
+ }
+
+ public void writeVbin16(byte[] bytes)
+ {
+ if (bytes == null) { bytes = new byte[0]; }
+ writeUint16(bytes.length);
+ put(ByteBuffer.wrap(bytes));
+ }
+
+ public void writeVbin32(byte[] bytes)
+ {
+ if (bytes == null) { bytes = new byte[0]; }
+ writeUint32(bytes.length);
+ put(ByteBuffer.wrap(bytes));
+ }
+
+ public void writeSequenceSet(RangeSet ranges)
+ {
+ if (ranges == null)
+ {
+ writeUint16((short) 0);
+ }
+ else
+ {
+ writeUint16(ranges.size() * 8);
+ for (Range range : ranges)
+ {
+ writeSequenceNo(range.getLower());
+ writeSequenceNo(range.getUpper());
+ }
+ }
+ }
+
+ public void writeByteRanges(RangeSet ranges)
+ {
+ throw new Error("not implemented");
+ }
+
+ public void writeUuid(UUID uuid)
+ {
+ long msb = 0;
+ long lsb = 0;
+ if (uuid != null)
+ {
+ msb = uuid.getMostSignificantBits();
+ lsb = uuid.getLeastSignificantBits();
+ }
+ writeUint64(msb);
+ writeUint64(lsb);
+ }
+
+ public void writeStruct(int type, Struct s)
+ {
+ boolean empty = false;
+ if (s == null)
+ {
+ s = Struct.create(type);
+ empty = true;
+ }
+
+ int width = s.getSizeWidth();
+ int pos = -1;
+ if (width > 0)
+ {
+ pos = beginSize(width);
+ }
+
+ if (type > 0)
+ {
+ writeUint16(type);
+ }
+
+ s.write(this);
+
+ if (width > 0)
+ {
+ endSize(width, pos);
+ }
+ }
+
+ public void writeStruct32(Struct s)
+ {
+ if (s == null)
+ {
+ writeUint32(0);
+ }
+ else
+ {
+ int pos = beginSize32();
+ writeUint16(s.getEncodedType());
+ s.write(this);
+ endSize32(pos);
+ }
+ }
+
+ private Type encoding(Object value)
+ {
+ if (value == null)
+ {
+ return Type.VOID;
+ }
+
+ Class klass = value.getClass();
+ Type type = resolve(klass);
+
+ if (type == null)
+ {
+ throw new IllegalArgumentException
+ ("unable to resolve type: " + klass + ", " + value);
+ }
+ else
+ {
+ return type;
+ }
+ }
+
+ static final Type resolve(Class klass)
+ {
+ Type type = ENCODINGS.get(klass);
+ if (type != null)
+ {
+ return type;
+ }
+
+ Class sup = klass.getSuperclass();
+ if (sup != null)
+ {
+ type = resolve(klass.getSuperclass());
+
+ if (type != null)
+ {
+ return type;
+ }
+ }
+
+ for (Class iface : klass.getInterfaces())
+ {
+ type = resolve(iface);
+ if (type != null)
+ {
+ return type;
+ }
+ }
+
+ return null;
+ }
+
+ public void writeMap(Map<String,Object> map)
+ {
+ int pos = beginSize32();
+ if (map != null)
+ {
+ writeUint32(map.size());
+ writeMapEntries(map);
+ }
+ endSize32(pos);
+ }
+
+ protected void writeMapEntries(Map<String,Object> map)
+ {
+ for (Map.Entry<String,Object> entry : map.entrySet())
+ {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ Type type = encoding(value);
+ writeStr8(key);
+ put(type.code);
+ write(type, value);
+ }
+ }
+
+ public void writeList(List<Object> list)
+ {
+ int pos = beginSize32();
+ if (list != null)
+ {
+ writeUint32(list.size());
+ writeListEntries(list);
+ }
+ endSize32(pos);
+ }
+
+ protected void writeListEntries(List<Object> list)
+ {
+ for (Object value : list)
+ {
+ Type type = encoding(value);
+ put(type.code);
+ write(type, value);
+ }
+ }
+
+ public void writeArray(List<Object> array)
+ {
+ int pos = beginSize32();
+ if (array != null)
+ {
+ writeArrayEntries(array);
+ }
+ endSize32(pos);
+ }
+
+ protected void writeArrayEntries(List<Object> array)
+ {
+ Type type;
+
+ if (array.isEmpty())
+ {
+ return;
+ }
+ else
+ {
+ type = encoding(array.get(0));
+ }
+
+ put(type.code);
+
+ writeUint32(array.size());
+
+ for (Object value : array)
+ {
+ write(type, value);
+ }
+ }
+
+ private void writeSize(Type t, int size)
+ {
+ if (t.fixed)
+ {
+ if (size != t.width)
+ {
+ throw new IllegalArgumentException
+ ("size does not match fixed width " + t.width + ": " +
+ size);
+ }
+ }
+ else
+ {
+ writeSize(t.width, size);
+ }
+ }
+
+ private void writeSize(int width, int size)
+ {
+ // XXX: should check lengths
+ switch (width)
+ {
+ case 1:
+ writeUint8((short) size);
+ break;
+ case 2:
+ writeUint16(size);
+ break;
+ case 4:
+ writeUint32(size);
+ break;
+ default:
+ throw new IllegalStateException("illegal width: " + width);
+ }
+ }
+
+ private int beginSize(int width)
+ {
+ switch (width)
+ {
+ case 1:
+ return beginSize8();
+ case 2:
+ return beginSize16();
+ case 4:
+ return beginSize32();
+ default:
+ throw new IllegalStateException("illegal width: " + width);
+ }
+ }
+
+ private void endSize(int width, int pos)
+ {
+ switch (width)
+ {
+ case 1:
+ endSize8(pos);
+ break;
+ case 2:
+ endSize16(pos);
+ break;
+ case 4:
+ endSize32(pos);
+ break;
+ default:
+ throw new IllegalStateException("illegal width: " + width);
+ }
+ }
+
+ private void writeBytes(Type t, byte[] bytes)
+ {
+ writeSize(t, bytes.length);
+ put(bytes);
+ }
+
+ private <T> T coerce(Class<T> klass, Object value)
+ {
+ if (klass.isInstance(value))
+ {
+ return klass.cast(value);
+ }
+ else
+ {
+ throw new IllegalArgumentException("" + value);
+ }
+ }
+
+ private void write(Type t, Object value)
+ {
+ switch (t)
+ {
+ case BIN8:
+ case UINT8:
+ writeUint8(coerce(Short.class, value));
+ break;
+ case INT8:
+ put(coerce(Byte.class, value));
+ break;
+ case CHAR:
+ put((byte) ((char)coerce(Character.class, value)));
+ break;
+ case BOOLEAN:
+ if (coerce(Boolean.class, value))
+ {
+ put((byte) 1);
+ }
+ else
+ {
+ put((byte) 0);
+ }
+ break;
+
+ case BIN16:
+ case UINT16:
+ writeUint16(coerce(Integer.class, value));
+ break;
+
+ case INT16:
+ writeUint16(coerce(Short.class, value));
+ break;
+
+ case BIN32:
+ case UINT32:
+ writeUint32(coerce(Long.class, value));
+ break;
+
+ case CHAR_UTF32:
+ case INT32:
+ writeUint32(coerce(Integer.class, value));
+ break;
+
+ case FLOAT:
+ writeUint32(Float.floatToIntBits(coerce(Float.class, value)));
+ break;
+
+ case BIN64:
+ case UINT64:
+ case INT64:
+ case DATETIME:
+ writeUint64(coerce(Long.class, value));
+ break;
+
+ case DOUBLE:
+ long bits = Double.doubleToLongBits(coerce(Double.class, value));
+ writeUint64(bits);
+ break;
+
+ case UUID:
+ writeUuid(coerce(UUID.class, value));
+ break;
+
+ case STR8:
+ writeStr8(coerce(String.class, value));
+ break;
+
+ case STR16:
+ writeStr16(coerce(String.class, value));
+ break;
+
+ case STR8_LATIN:
+ case STR8_UTF16:
+ case STR16_LATIN:
+ case STR16_UTF16:
+ // XXX: need to do character conversion
+ writeBytes(t, coerce(String.class, value).getBytes());
+ break;
+
+ case MAP:
+ writeMap((Map<String,Object>) coerce(Map.class, value));
+ break;
+ case LIST:
+ writeList(coerce(List.class, value));
+ break;
+ case ARRAY:
+ writeArray(coerce(List.class, value));
+ break;
+ case STRUCT32:
+ writeStruct32(coerce(Struct.class, value));
+ break;
+
+ case BIN40:
+ case DEC32:
+ case BIN72:
+ case DEC64:
+ // XXX: what types are we supposed to use here?
+ writeBytes(t, coerce(byte[].class, value));
+ break;
+
+ case VOID:
+ break;
+
+ default:
+ writeBytes(t, coerce(byte[].class, value));
+ break;
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java
new file mode 100644
index 0000000000..6f7a2fa3b2
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java
@@ -0,0 +1,144 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.qpid.transport.Binary;
+
+/**
+ * Byte Buffer Decoder.
+ * Decoder concrete implementor using a backing byte buffer for decoding data.
+ *
+ * @author Rafael H. Schloming
+ */
+public final class BBDecoder extends AbstractDecoder
+{
+ private ByteBuffer in;
+
+ public void init(ByteBuffer in)
+ {
+ this.in = in;
+ this.in.order(ByteOrder.BIG_ENDIAN);
+ }
+
+ protected byte doGet()
+ {
+ return in.get();
+ }
+
+ protected void doGet(byte[] bytes)
+ {
+ in.get(bytes);
+ }
+
+ protected Binary get(int size)
+ {
+ if (in.hasArray())
+ {
+ byte[] bytes = in.array();
+ Binary bin = new Binary(bytes, in.arrayOffset() + in.position(), size);
+ in.position(in.position() + size);
+ return bin;
+ }
+ else
+ {
+ return super.get(size);
+ }
+ }
+
+ public boolean hasRemaining()
+ {
+ return in.hasRemaining();
+ }
+
+ public short readUint8()
+ {
+ return (short) (0xFF & in.get());
+ }
+
+ public int readUint16()
+ {
+ return 0xFFFF & in.getShort();
+ }
+
+ public long readUint32()
+ {
+ return 0xFFFFFFFFL & in.getInt();
+ }
+
+ public long readUint64()
+ {
+ return in.getLong();
+ }
+
+ public byte[] readBin128()
+ {
+ byte[] result = new byte[16];
+ get(result);
+ return result;
+ }
+
+ public byte[] readBytes(int howManyBytes)
+ {
+ byte[] result = new byte[howManyBytes];
+ get(result);
+ return result;
+ }
+
+ public double readDouble()
+ {
+ return in.getDouble();
+ }
+
+ public float readFloat()
+ {
+ return in.getFloat();
+ }
+
+ public short readInt16()
+ {
+ return in.getShort();
+ }
+
+ public int readInt32()
+ {
+ return in.getInt();
+ }
+
+ public byte readInt8()
+ {
+ return in.get();
+ }
+
+ public byte[] readReaminingBytes()
+ {
+ byte[] result = new byte[in.limit() - in.position()];
+ get(result);
+ return result;
+ }
+
+ public long readInt64()
+ {
+ return in.getLong();
+ }
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java
new file mode 100644
index 0000000000..532c19ec18
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java
@@ -0,0 +1,323 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+/**
+ * Byte Buffer Encoder.
+ * Encoder concrete implementor using a backing byte buffer for encoding data.
+ *
+ * @author Rafael H. Schloming
+ */
+public final class BBEncoder extends AbstractEncoder
+{
+ private ByteBuffer out;
+ private int segment;
+
+ public BBEncoder(int capacity) {
+ out = ByteBuffer.allocate(capacity);
+ out.order(ByteOrder.BIG_ENDIAN);
+ segment = 0;
+ }
+
+ public void init()
+ {
+ out.clear();
+ segment = 0;
+ }
+
+ public ByteBuffer segment()
+ {
+ int pos = out.position();
+ out.position(segment);
+ ByteBuffer slice = out.slice();
+ slice.limit(pos - segment);
+ out.position(pos);
+ segment = pos;
+ return slice;
+ }
+
+ private void grow(int size)
+ {
+ ByteBuffer old = out;
+ int capacity = old.capacity();
+ out = ByteBuffer.allocate(Math.max(capacity + size, 2*capacity));
+ out.order(ByteOrder.BIG_ENDIAN);
+ out.put(old);
+ }
+
+ protected void doPut(byte b)
+ {
+ try
+ {
+ out.put(b);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(1);
+ out.put(b);
+ }
+ }
+
+ protected void doPut(ByteBuffer src)
+ {
+ try
+ {
+ out.put(src);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(src.remaining());
+ out.put(src);
+ }
+ }
+
+ protected void put(byte[] bytes)
+ {
+ try
+ {
+ out.put(bytes);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(bytes.length);
+ out.put(bytes);
+ }
+ }
+
+ public void writeUint8(short b)
+ {
+ assert b < 0x100;
+
+ try
+ {
+ out.put((byte) b);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(1);
+ out.put((byte) b);
+ }
+ }
+
+ public void writeUint16(int s)
+ {
+ assert s < 0x10000;
+
+ try
+ {
+ out.putShort((short) s);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(2);
+ out.putShort((short) s);
+ }
+ }
+
+ public void writeUint32(long i)
+ {
+ assert i < 0x100000000L;
+
+ try
+ {
+ out.putInt((int) i);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(4);
+ out.putInt((int) i);
+ }
+ }
+
+ public void writeUint64(long l)
+ {
+ try
+ {
+ out.putLong(l);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(8);
+ out.putLong(l);
+ }
+ }
+
+ public int beginSize8()
+ {
+ int pos = out.position();
+ try
+ {
+ out.put((byte) 0);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(1);
+ out.put((byte) 0);
+ }
+ return pos;
+ }
+
+ public void endSize8(int pos)
+ {
+ int cur = out.position();
+ out.put(pos, (byte) (cur - pos - 1));
+ }
+
+ public int beginSize16()
+ {
+ int pos = out.position();
+ try
+ {
+ out.putShort((short) 0);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(2);
+ out.putShort((short) 0);
+ }
+ return pos;
+ }
+
+ public void endSize16(int pos)
+ {
+ int cur = out.position();
+ out.putShort(pos, (short) (cur - pos - 2));
+ }
+
+ public int beginSize32()
+ {
+ int pos = out.position();
+ try
+ {
+ out.putInt(0);
+ }
+ catch (BufferOverflowException e)
+ {
+ grow(4);
+ out.putInt(0);
+ }
+ return pos;
+ }
+
+ public void endSize32(int pos)
+ {
+ int cur = out.position();
+ out.putInt(pos, (cur - pos - 4));
+ }
+
+ public void writeDouble(double aDouble)
+ {
+ try
+ {
+ out.putDouble(aDouble);
+ } catch(BufferOverflowException exception)
+ {
+ grow(8);
+ out.putDouble(aDouble);
+ }
+ }
+
+ public void writeInt16(short aShort)
+ {
+ try
+ {
+ out.putShort(aShort);
+ } catch(BufferOverflowException exception)
+ {
+ grow(2);
+ out.putShort(aShort);
+ }
+ }
+
+ public void writeInt32(int anInt)
+ {
+ try
+ {
+ out.putInt(anInt);
+ } catch(BufferOverflowException exception)
+ {
+ grow(4);
+ out.putInt(anInt);
+ }
+ }
+
+ public void writeInt64(long aLong)
+ {
+ try
+ {
+ out.putLong(aLong);
+ } catch(BufferOverflowException exception)
+ {
+ grow(8);
+ out.putLong(aLong);
+ }
+ }
+
+ public void writeInt8(byte aByte)
+ {
+ try
+ {
+ out.put(aByte);
+ } catch(BufferOverflowException exception)
+ {
+ grow(1);
+ out.put(aByte);
+ }
+ }
+
+ public void writeBin128(byte[] byteArray)
+ {
+ byteArray = (byteArray != null) ? byteArray : new byte [16];
+
+ assert byteArray.length == 16;
+
+ try
+ {
+ out.put(byteArray);
+ } catch(BufferOverflowException exception)
+ {
+ grow(16);
+ out.put(byteArray);
+ }
+ }
+
+ public void writeFloat(float aFloat)
+ {
+ try
+ {
+ out.putFloat(aFloat);
+ } catch(BufferOverflowException exception)
+ {
+ grow(4);
+ out.putFloat(aFloat);
+ }
+ }
+
+ public void writeMagicNumber()
+ {
+ out.put("AM2".getBytes());
+ }
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java
new file mode 100644
index 0000000000..a4df5b5fcb
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java
@@ -0,0 +1,283 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.transport.RangeSet;
+import org.apache.qpid.transport.Struct;
+
+
+/**
+ * Decoder interface.
+ * Each concrete implementor must specify how to decode given values.
+ *
+ * @author Rafael H. Schloming
+ */
+public interface Decoder
+{
+ /**
+ * Tells whether there are any remaining byte(s) to be read.
+ *
+ * @return true if there are remaining bytes, false otherwise.
+ */
+ boolean hasRemaining();
+
+ /**
+ * The uint8 type is an 8-bit unsigned integral value.
+ *
+ * @return an 8-bit unsigned integral value.
+ */
+ short readUint8();
+
+ /**
+ *The uint16 type is a 16-bit unsigned integral value encoded in network byte order.
+ *
+ * @return a 16-bit unsigned integral value encoded in network byte order.
+ */
+ int readUint16();
+
+ /**
+ *The uint32 type is a 32-bit unsigned integral value encoded in network byte order.
+ *
+ * @return a 32-bit unsigned integral value encoded in network byte order.
+ */
+ long readUint32();
+
+ /**
+ * The uint64 type is a 64-bit unsigned integral value encoded in network byte order.
+ *
+ * @return a 64-bit unsigned integral value encoded in network byte order.
+ */
+ long readUint64();
+
+ /**
+ * The datetime type encodes a date and time using the 64 bit POSIX time_t format.
+ *
+ * @return a date and time using the 64 bit POSIX time_t format.
+ */
+ long readDatetime();
+
+ /**
+ * The uuid type encodes a universally unique id as defined by RFC-4122.
+ * The format and operations for this type can be found in section 4.1.2 of RFC-4122.
+ *
+ * return a universally unique id as defined by RFC-4122.
+ */
+ UUID readUuid();
+
+ /**
+// *The sequence-no type encodes, in network byte order, a serial number as defined in RFC-1982.
+ *
+ * @return a serial number as defined in RFC-1982.
+ */
+ int readSequenceNo();
+
+ RangeSet readSequenceSet(); // XXX
+ RangeSet readByteRanges(); // XXX
+
+ /**
+ * The str8 type encodes up to 255 octets worth of UTF-8 unicode.
+ * The number of octets of unicode is first encoded as an 8-bit unsigned integral value.
+ * This is followed by the actual UTF-8 unicode.
+ * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of characters since
+ * the UTF-8 unicode may include multi-byte character sequences.
+ *
+ * @return a string.
+ */
+ String readStr8();
+
+ /**
+ * The str16 type encodes up to 65535 octets worth of UTF-8 unicode.
+ * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order.
+ * This is followed by the actual UTF-8 unicode.
+ * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of unicode
+ * characters since the UTF-8 unicode may include multi-byte character sequences.
+ *
+ * return a string.
+ */
+ String readStr16();
+
+ /**
+ * The vbin8 type encodes up to 255 octets of opaque binary data.
+ *
+ * return a byte array.
+ */
+ byte[] readVbin8();
+
+ /**
+ * The vbin16 type encodes up to 65535 octets of opaque binary data.
+ *
+ * @return the corresponding byte array.
+ */
+ byte[] readVbin16();
+
+ /**
+ * The vbin32 type encodes up to 4294967295 octets of opaque binary data.
+ *
+ * @return the corresponding byte array.
+ */
+ byte[] readVbin32();
+
+ /**
+ * The struct32 type describes any coded struct with a 32-bit (4 octet) size.
+ * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded
+ * value for this type MUST always contain the size, class-code, and struct-code in that order.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the
+ * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any
+ * valid class code.
+ * The struct-code is a single octet that may be set to any valid struct code within the given class-code.
+ * The first six octets are then followed by the packing flags and encoded field data.
+ * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition
+ * identified with the encoded class-code and struct-code.
+ *
+ * @return the decoded struct.
+ */
+ Struct readStruct32();
+
+ /**
+ * A map is a set of distinct keys where each key has an associated (type,value) pair.
+ * The triple of the key, type, and value, form an entry within a map. Each entry within a given map MUST have a
+ * distinct key.
+ * A map is encoded as a size in octets, a count of the number of entries, followed by the encoded entries themselves.
+ * An encoded map may contain up to (4294967295 - 4) octets worth of encoded entries.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of
+ * encoded entries plus 4. (The extra 4 octets is added for the entry count.)
+ * The size is then followed by the number of entries encoded as a 32-bit unsigned integral value in network byte order.
+ * Finally the entries are encoded sequentially.
+ * An entry is encoded as the key, followed by the type, and then the value. The key is always a string encoded as a str8.
+ * The type is a single octet that may contain any valid AMQP type code.
+ * The value is encoded according to the rules defined by the type code for that entry.
+ *
+ * @return the decoded map.
+ */
+ Map<String,Object> readMap();
+
+ /**
+ * A list is an ordered sequence of (type, value) pairs. The (type, value) pair forms an item within the list.
+ * The list may contain items of many distinct types. A list is encoded as a size in octets, followed by a count of the
+ * number of items, followed by the items themselves encoded in their defined order.
+ * An encoded list may contain up to (4294967295 - 4) octets worth of encoded items.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth
+ * of encoded items plus 4. (The extra4 octets is added for the item count.)
+ * The size is then followed by the number of items encoded as a 32-bit unsigned integral value in network byte order.
+ * Finally the items are encoded sequentially in their defined order.
+ * An item is encoded as the type followed by the value. The type is a single octet that may contain any valid AMQP type
+ * code.
+ * The value is encoded according to the rules defined by the type code for that item.
+ *
+ * @return the decoded list.
+ */
+ List<Object> readList();
+
+ /**
+ * An array is an ordered sequence of values of the same type.
+ * The array is encoded in as a size in octets, followed by a type code, then a count of the number values in the array,
+ * and finally the values encoded in their defined order.
+ * An encoded array may contain up to (4294967295 - 5) octets worth of encoded values.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of
+ * encoded values plus 5. (The extra 5 octets consist of 4 octets for the count of the number of values, and one octet to
+ * hold the type code for the items inthe array.)
+ * The size is then followed by a single octet that may contain any valid AMQP type code.
+ * The type code is then followed by the number of values encoded as a 32-bit unsigned integral value in network byte
+ * order.
+ * Finally the values are encoded sequentially in their defined order according to the rules defined by the type code for
+ * the array.
+ *
+ * @return the decoded array.
+ */
+ List<Object> readArray();
+
+ /**
+ *
+ * @param type the type of the struct.
+ * @return the decoded struct.
+ */
+ Struct readStruct(int type);
+
+ /**
+ * The float type encodes a single precision 32-bit floating point number.
+ * The format and operations are defined by the IEEE 754 standard for 32-bit single precision floating point numbers.
+ *
+ * @return the decoded float.
+ */
+ float readFloat();
+
+ /**
+ * The double type encodes a double precision 64-bit floating point number.
+ * The format and operations are defined by the IEEE 754 standard for 64-bit double precision floating point numbers.
+ *
+ * @return the decoded double
+ */
+ double readDouble();
+
+ /**
+ * The int8 type is a signed integral value encoded using an 8-bit two's complement representation.
+ *
+ * @return the decoded integer.
+ */
+ byte readInt8();
+
+ /**
+ * The int16 type is a signed integral value encoded using a 16-bit two's complement representation in network byte order.
+ *
+ * @return the decoded integer.
+ */
+ short readInt16();
+
+ /**
+ * The int32 type is a signed integral value encoded using a 32-bit two's complement representation in network byte order.
+ *
+ * @return the decoded integer.
+ */
+ int readInt32();
+
+ /**
+ * The int64 type is a signed integral value encoded using a 64-bit two's complement representation in network byte order.
+ *
+ * @return the decoded integer (as long).
+ */
+ long readInt64();
+
+ /**
+ * The bin128 type consists of 16 consecutive octets of opaque binary data.
+ *
+ * @return the decoded byte array.
+ */
+ byte [] readBin128();
+
+ /**
+ * Reads the remaining bytes on the underlying buffer.
+ *
+ * @return the remaining bytes on the underlying buffer.
+ */
+ byte[] readReaminingBytes ();
+
+ /**
+ * Reads the given number of bytes.
+ *
+ * @param howManyBytes how many bytes need to be read?
+ * @return a byte array containing the requested data.
+ */
+ byte[] readBytes (int howManyBytes);
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java
new file mode 100644
index 0000000000..37ce8a5cb7
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.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.transport.codec;
+
+
+/**
+ * Encodable
+ *
+ * @author Rafael H. Schloming
+ */
+public interface Encodable
+{
+ /**
+ * Encodes this encodable using the given encoder.
+ *
+ * @param encoder the encoder.
+ */
+ void write(Encoder encoder);
+
+ /**
+ * Decodes this encodable using the given decoder.
+ *
+ * @param decoder the decoder.
+ */
+ void read(Decoder decoder);
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java
new file mode 100644
index 0000000000..7d4f02af31
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java
@@ -0,0 +1,282 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.codec;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.transport.RangeSet;
+import org.apache.qpid.transport.Struct;
+
+
+/**
+ * Encoder interface.
+ * Each concrete implementor must specify how to encode given values.
+ *
+ * @author Rafael H. Schloming
+ */
+public interface Encoder
+{
+ /**
+ * The uint8 type is an 8-bit unsigned integral value.
+ *
+ * @param b the unsigned integer to be encoded.
+ */
+ void writeUint8(short b);
+
+ /**
+ *The uint16 type is a 16-bit unsigned integral value encoded in network byte order.
+ *
+ * @param s the unsigned integer to be encoded.
+ */
+ void writeUint16(int s);
+
+ /**
+ *The uint32 type is a 32-bit unsigned integral value encoded in network byte order.
+ *
+ * @param i the unsigned integer to be encoded.
+ */
+ void writeUint32(long i);
+
+ /**
+ * The uint64 type is a 64-bit unsigned integral value encoded in network byte order.
+ *
+ * @param b the unsigned integer to be encoded.
+ */
+ void writeUint64(long l);
+
+ /**
+ * The datetime type encodes a date and time using the 64 bit POSIX time_t format.
+ *
+ * @param l the datetime (as long) to be encoded.
+ */
+ void writeDatetime(long l);
+
+ /**
+ * The uuid type encodes a universally unique id as defined by RFC-4122.
+ * The format and operations for this type can be found in section 4.1.2 of RFC-4122.
+ *
+ * @param uuid the uuid to be encoded.
+ */
+ void writeUuid(UUID uuid);
+
+ /**
+ *The sequence-no type encodes, in network byte order, a serial number as defined in RFC-1982.
+ *
+ * @param s the sequence number to be encoded.
+ */
+ void writeSequenceNo(int s);
+
+ void writeSequenceSet(RangeSet ranges); // XXX
+ void writeByteRanges(RangeSet ranges); // XXX
+
+ /**
+ * The str8 type encodes up to 255 octets worth of UTF-8 unicode.
+ * The number of octets of unicode is first encoded as an 8-bit unsigned integral value.
+ * This is followed by the actual UTF-8 unicode.
+ * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of characters since
+ * the UTF-8 unicode may include multi-byte character sequences.
+ *
+ * @param s the string to be encoded.
+ */
+ void writeStr8(String s);
+
+ /**
+ * The str16 type encodes up to 65535 octets worth of UTF-8 unicode.
+ * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order.
+ * This is followed by the actual UTF-8 unicode.
+ * Note that the encoded size refers to the number of octets of unicode, not necessarily the number of unicode
+ * characters since the UTF-8 unicode may include multi-byte character sequences.
+ *
+ * @param s the string to be encoded.
+ */
+ void writeStr16(String s);
+
+ /**
+ * The vbin8 type encodes up to 255 octets of opaque binary data.
+ * The number of octets is first encoded as an 8-bit unsigned integral value.
+ * This is followed by the actual data.
+ *
+ * @param bytes the byte array to be encoded.
+ */
+ void writeVbin8(byte[] bytes);
+
+ /**
+ * The vbin16 type encodes up to 65535 octets of opaque binary data.
+ * The number of octets is first encoded as a 16-bit unsigned integral value in network byte order.
+ * This is followed by the actual data.
+ *
+ * @param bytes the byte array to be encoded.
+ */
+ void writeVbin16(byte[] bytes);
+
+ /**
+ * The vbin32 type encodes up to 4294967295 octets of opaque binary data.
+ * The number of octets is first encoded as a 32-bit unsigned integral value in network byte order.
+ * This is followed by the actual data.
+ *
+ * @param bytes the byte array to be encoded.
+ */
+ void writeVbin32(byte[] bytes);
+
+ /**
+ * The struct32 type describes any coded struct with a 32-bit (4 octet) size.
+ * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded
+ * value for this type MUST always contain the size, class-code, and struct-code in that order.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the
+ * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any
+ * valid class code.
+ * The struct-code is a single octet that may be set to any valid struct code within the given class-code.
+ * The first six octets are then followed by the packing flags and encoded field data.
+ * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition
+ * identified with the encoded class-code and struct-code.
+ *
+ * @param struct the struct to be encoded.
+ */
+ void writeStruct32(Struct struct);
+
+ /**
+ * A map is a set of distinct keys where each key has an associated (type,value) pair.
+ * The triple of the key, type, and value, form an entry within a map. Each entry within a given map MUST have a
+ * distinct key.
+ * A map is encoded as a size in octets, a count of the number of entries, followed by the encoded entries themselves.
+ * An encoded map may contain up to (4294967295 - 4) octets worth of encoded entries.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of
+ * encoded entries plus 4. (The extra 4 octets is added for the entry count.)
+ * The size is then followed by the number of entries encoded as a 32-bit unsigned integral value in network byte order.
+ * Finally the entries are encoded sequentially.
+ * An entry is encoded as the key, followed by the type, and then the value. The key is always a string encoded as a str8.
+ * The type is a single octet that may contain any valid AMQP type code.
+ * The value is encoded according to the rules defined by the type code for that entry.
+ *
+ * @param map the map to be encoded.
+ */
+ void writeMap(Map<String,Object> map);
+
+ /**
+ * A list is an ordered sequence of (type, value) pairs. The (type, value) pair forms an item within the list.
+ * The list may contain items of many distinct types. A list is encoded as a size in octets, followed by a count of the
+ * number of items, followed by the items themselves encoded in their defined order.
+ * An encoded list may contain up to (4294967295 - 4) octets worth of encoded items.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth
+ * of encoded items plus 4. (The extra4 octets is added for the item count.)
+ * The size is then followed by the number of items encoded as a 32-bit unsigned integral value in network byte order.
+ * Finally the items are encoded sequentially in their defined order.
+ * An item is encoded as the type followed by the value. The type is a single octet that may contain any valid AMQP type
+ * code.
+ * The value is encoded according to the rules defined by the type code for that item.
+ *
+ * @param list the list to be encoded.
+ */
+ void writeList(List<Object> list);
+
+ /**
+ * An array is an ordered sequence of values of the same type.
+ * The array is encoded in as a size in octets, followed by a type code, then a count of the number values in the array,
+ * and finally the values encoded in their defined order.
+ * An encoded array may contain up to (4294967295 - 5) octets worth of encoded values.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order equal to the number of octets worth of
+ * encoded values plus 5. (The extra 5 octets consist of 4 octets for the count of the number of values, and one octet to
+ * hold the type code for the items inthe array.)
+ * The size is then followed by a single octet that may contain any valid AMQP type code.
+ * The type code is then followed by the number of values encoded as a 32-bit unsigned integral value in network byte
+ * order.
+ * Finally the values are encoded sequentially in their defined order according to the rules defined by the type code for
+ * the array.
+ *
+ * @param array the array to be encoded.
+ */
+ void writeArray(List<Object> array);
+
+ /**
+ * The struct32 type describes any coded struct with a 32-bit (4 octet) size.
+ * The type is restricted to be only coded structs with a 32-bit size, consequently the first six octets of any encoded
+ * value for this type MUST always contain the size, class-code, and struct-code in that order.
+ * The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to the size of the
+ * encoded field-data, packing-flags, class-code, and struct-code. The class-code is a single octet that may be set to any
+ * valid class code.
+ * The struct-code is a single octet that may be set to any valid struct code within the given class-code.
+ * The first six octets are then followed by the packing flags and encoded field data.
+ * The presence and quantity of packingflags, as well as the specific fields are determined by the struct definition
+ * identified with the encoded class-code and struct-code.
+ *
+ * @param type the type of the struct.
+ * @param struct the struct to be encoded.
+ */
+ void writeStruct(int type, Struct struct);
+
+ /**
+ * The float type encodes a single precision 32-bit floating point number.
+ * The format and operations are defined by the IEEE 754 standard for 32-bit single precision floating point numbers.
+ *
+ * @param aFloat the float to be encoded.
+ */
+ void writeFloat(float aFloat);
+
+ /**
+ * The double type encodes a double precision 64-bit floating point number.
+ * The format and operations are defined by the IEEE 754 standard for 64-bit double precision floating point numbers.
+ *
+ * @param aDouble the double to be encoded.
+ */
+ void writeDouble(double aDouble);
+
+ /**
+ * The int8 type is a signed integral value encoded using an 8-bit two's complement representation.
+ *
+ * @param aByte the integer to be encoded.
+ */
+ void writeInt8(byte aByte);
+
+ /**
+ * The int16 type is a signed integral value encoded using a 16-bit two's complement representation in network byte order.
+ *
+ * @param aShort the integer to be encoded.
+ */
+ void writeInt16(short aShort);
+
+ /**
+ * The int32 type is a signed integral value encoded using a 32-bit two's complement representation in network byte order.
+ *
+ * @param anInt the integer to be encoded.
+ */
+ void writeInt32(int anInt);
+
+ /**
+ * The int64 type is a signed integral value encoded using a 64-bit two's complement representation in network byte order.
+ *
+ * @param aLong the integer to be encoded.
+ */
+ void writeInt64(long aLong);
+
+ /**
+ * The bin128 type consists of 16 consecutive octets of opaque binary data.
+ *
+ * @param bytes the bytes array to be encoded.
+ */
+ void writeBin128(byte [] bytes);
+
+ /**
+ * Encodes the AMQP magic number.
+ */
+ void writeMagicNumber();
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java
new file mode 100644
index 0000000000..33d552b91e
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java
@@ -0,0 +1,226 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.transport.codec.BBDecoder;
+import org.apache.qpid.transport.codec.Decoder;
+
+import org.apache.qpid.transport.Header;
+import org.apache.qpid.transport.Method;
+import org.apache.qpid.transport.ProtocolError;
+import org.apache.qpid.transport.ProtocolEvent;
+import org.apache.qpid.transport.ProtocolHeader;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.SegmentType;
+import org.apache.qpid.transport.Struct;
+
+
+/**
+ * Assembler
+ *
+ */
+
+public class Assembler implements Receiver<NetworkEvent>, NetworkDelegate
+{
+
+ private final Receiver<ProtocolEvent> receiver;
+ private final Map<Integer,List<Frame>> segments;
+ private final Method[] incomplete;
+ private final ThreadLocal<BBDecoder> decoder = new ThreadLocal<BBDecoder>()
+ {
+ public BBDecoder initialValue()
+ {
+ return new BBDecoder();
+ }
+ };
+
+ public Assembler(Receiver<ProtocolEvent> receiver)
+ {
+ this.receiver = receiver;
+ segments = new HashMap<Integer,List<Frame>>();
+ incomplete = new Method[64*1024];
+ }
+
+ private int segmentKey(Frame frame)
+ {
+ return (frame.getTrack() + 1) * frame.getChannel();
+ }
+
+ private List<Frame> getSegment(Frame frame)
+ {
+ return segments.get(segmentKey(frame));
+ }
+
+ private void setSegment(Frame frame, List<Frame> segment)
+ {
+ int key = segmentKey(frame);
+ if (segments.containsKey(key))
+ {
+ error(new ProtocolError(Frame.L2, "segment in progress: %s",
+ frame));
+ }
+ segments.put(segmentKey(frame), segment);
+ }
+
+ private void clearSegment(Frame frame)
+ {
+ segments.remove(segmentKey(frame));
+ }
+
+ private void emit(int channel, ProtocolEvent event)
+ {
+ event.setChannel(channel);
+ receiver.received(event);
+ }
+
+ public void received(NetworkEvent event)
+ {
+ event.delegate(this);
+ }
+
+ public void exception(Throwable t)
+ {
+ this.receiver.exception(t);
+ }
+
+ public void closed()
+ {
+ this.receiver.closed();
+ }
+
+ public void init(ProtocolHeader header)
+ {
+ emit(0, header);
+ }
+
+ public void error(ProtocolError error)
+ {
+ emit(0, error);
+ }
+
+ public void frame(Frame frame)
+ {
+ ByteBuffer segment;
+ if (frame.isFirstFrame() && frame.isLastFrame())
+ {
+ segment = frame.getBody();
+ assemble(frame, segment);
+ }
+ else
+ {
+ List<Frame> frames;
+ if (frame.isFirstFrame())
+ {
+ frames = new ArrayList<Frame>();
+ setSegment(frame, frames);
+ }
+ else
+ {
+ frames = getSegment(frame);
+ }
+
+ frames.add(frame);
+
+ if (frame.isLastFrame())
+ {
+ clearSegment(frame);
+
+ int size = 0;
+ for (Frame f : frames)
+ {
+ size += f.getSize();
+ }
+ segment = ByteBuffer.allocate(size);
+ for (Frame f : frames)
+ {
+ segment.put(f.getBody());
+ }
+ segment.flip();
+ assemble(frame, segment);
+ }
+ }
+
+ }
+
+ private void assemble(Frame frame, ByteBuffer segment)
+ {
+ BBDecoder dec = decoder.get();
+ dec.init(segment);
+
+ int channel = frame.getChannel();
+ Method command;
+
+ switch (frame.getType())
+ {
+ case CONTROL:
+ int controlType = dec.readUint16();
+ Method control = Method.create(controlType);
+ control.read(dec);
+ emit(channel, control);
+ break;
+ case COMMAND:
+ int commandType = dec.readUint16();
+ // read in the session header, right now we don't use it
+ dec.readUint16();
+ command = Method.create(commandType);
+ command.read(dec);
+ if (command.hasPayload())
+ {
+ incomplete[channel] = command;
+ }
+ else
+ {
+ emit(channel, command);
+ }
+ break;
+ case HEADER:
+ command = incomplete[channel];
+ List<Struct> structs = new ArrayList(2);
+ while (dec.hasRemaining())
+ {
+ structs.add(dec.readStruct32());
+ }
+ command.setHeader(new Header(structs));
+ if (frame.isLastSegment())
+ {
+ incomplete[channel] = null;
+ emit(channel, command);
+ }
+ break;
+ case BODY:
+ command = incomplete[channel];
+ command.setBody(segment);
+ incomplete[channel] = null;
+ emit(channel, command);
+ break;
+ default:
+ throw new IllegalStateException("unknown frame type: " + frame.getType());
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java
new file mode 100644
index 0000000000..8a2aba2e6d
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.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.transport.network;
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.transport.Binding;
+import org.apache.qpid.transport.Connection;
+import org.apache.qpid.transport.ConnectionDelegate;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.Sender;
+
+/**
+ * ConnectionBinding
+ *
+ */
+
+public abstract class ConnectionBinding
+ implements Binding<Connection,ByteBuffer>
+{
+
+ public static Binding<Connection,ByteBuffer> get(final Connection connection)
+ {
+ return new ConnectionBinding()
+ {
+ public Connection connection()
+ {
+ return connection;
+ }
+ };
+ }
+
+ public static Binding<Connection,ByteBuffer> get(final ConnectionDelegate delegate)
+ {
+ return new ConnectionBinding()
+ {
+ public Connection connection()
+ {
+ Connection conn = new Connection();
+ conn.setConnectionDelegate(delegate);
+ return conn;
+ }
+ };
+ }
+
+ public static final int MAX_FRAME_SIZE = 64 * 1024 - 1;
+
+ public abstract Connection connection();
+
+ public Connection endpoint(Sender<ByteBuffer> sender)
+ {
+ Connection conn = connection();
+
+ // XXX: hardcoded max-frame
+ Disassembler dis = new Disassembler(sender, MAX_FRAME_SIZE);
+ conn.setSender(dis);
+ return conn;
+ }
+
+ public Receiver<ByteBuffer> receiver(Connection conn)
+ {
+ return new InputHandler(new Assembler(conn));
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java
new file mode 100644
index 0000000000..7908700cbe
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java
@@ -0,0 +1,239 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network;
+
+import org.apache.qpid.transport.codec.BBEncoder;
+
+import org.apache.qpid.transport.Header;
+import org.apache.qpid.transport.Method;
+import org.apache.qpid.transport.ProtocolDelegate;
+import org.apache.qpid.transport.ProtocolError;
+import org.apache.qpid.transport.ProtocolEvent;
+import org.apache.qpid.transport.ProtocolHeader;
+import org.apache.qpid.transport.SegmentType;
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.Struct;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static org.apache.qpid.transport.network.Frame.*;
+
+import static java.lang.Math.*;
+
+
+/**
+ * Disassembler
+ *
+ */
+
+public final class Disassembler implements Sender<ProtocolEvent>,
+ ProtocolDelegate<Void>
+{
+
+ private final Sender<ByteBuffer> sender;
+ private final int maxPayload;
+ private final ByteBuffer header;
+ private final Object sendlock = new Object();
+ private final ThreadLocal<BBEncoder> encoder = new ThreadLocal()
+ {
+ public BBEncoder initialValue()
+ {
+ return new BBEncoder(4*1024);
+ }
+ };
+
+ public Disassembler(Sender<ByteBuffer> sender, int maxFrame)
+ {
+ if (maxFrame <= HEADER_SIZE || maxFrame >= 64*1024)
+ {
+ throw new IllegalArgumentException
+ ("maxFrame must be > HEADER_SIZE and < 64K: " + maxFrame);
+ }
+ this.sender = sender;
+ this.maxPayload = maxFrame - HEADER_SIZE;
+ this.header = ByteBuffer.allocate(HEADER_SIZE);
+ this.header.order(ByteOrder.BIG_ENDIAN);
+
+ }
+
+ public void send(ProtocolEvent event)
+ {
+ event.delegate(null, this);
+ }
+
+ public void flush()
+ {
+ synchronized (sendlock)
+ {
+ sender.flush();
+ }
+ }
+
+ public void close()
+ {
+ synchronized (sendlock)
+ {
+ sender.close();
+ }
+ }
+
+ private final void frame(byte flags, byte type, byte track, int channel, int size, ByteBuffer buf)
+ {
+ synchronized (sendlock)
+ {
+ header.put(0, flags);
+ header.put(1, type);
+ header.putShort(2, (short) (size + HEADER_SIZE));
+ header.put(5, track);
+ header.putShort(6, (short) channel);
+
+ header.rewind();
+
+ sender.send(header);
+
+ int limit = buf.limit();
+ buf.limit(buf.position() + size);
+ sender.send(buf);
+ buf.limit(limit);
+ }
+ }
+
+ private void fragment(byte flags, SegmentType type, ProtocolEvent event,
+ ByteBuffer buf)
+ {
+ byte typeb = (byte) type.getValue();
+ byte track = event.getEncodedTrack() == Frame.L4 ? (byte) 1 : (byte) 0;
+
+ int remaining = buf.remaining();
+ boolean first = true;
+ while (true)
+ {
+ int size = min(maxPayload, remaining);
+ remaining -= size;
+
+ byte newflags = flags;
+ if (first)
+ {
+ newflags |= FIRST_FRAME;
+ first = false;
+ }
+ if (remaining == 0)
+ {
+ newflags |= LAST_FRAME;
+ }
+
+ frame(newflags, typeb, track, event.getChannel(), size, buf);
+
+ if (remaining == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ public void init(Void v, ProtocolHeader header)
+ {
+ synchronized (sendlock)
+ {
+ sender.send(header.toByteBuffer());
+ sender.flush();
+ }
+ }
+
+ public void control(Void v, Method method)
+ {
+ method(method, SegmentType.CONTROL);
+ }
+
+ public void command(Void v, Method method)
+ {
+ method(method, SegmentType.COMMAND);
+ }
+
+ private ByteBuffer copy(ByteBuffer src)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(src.remaining());
+ buf.put(src);
+ buf.flip();
+ return buf;
+ }
+
+ private void method(Method method, SegmentType type)
+ {
+ BBEncoder enc = encoder.get();
+ enc.init();
+ enc.writeUint16(method.getEncodedType());
+ if (type == SegmentType.COMMAND)
+ {
+ if (method.isSync())
+ {
+ enc.writeUint16(0x0101);
+ }
+ else
+ {
+ enc.writeUint16(0x0100);
+ }
+ }
+ method.write(enc);
+ ByteBuffer methodSeg = enc.segment();
+
+ byte flags = FIRST_SEG;
+
+ boolean payload = method.hasPayload();
+ if (!payload)
+ {
+ flags |= LAST_SEG;
+ }
+
+ ByteBuffer headerSeg = null;
+ if (payload)
+ {
+ final Header hdr = method.getHeader();
+ if (hdr != null)
+ {
+ final Struct[] structs = hdr.getStructs();
+
+ for (Struct st : structs)
+ {
+ enc.writeStruct32(st);
+ }
+ }
+ headerSeg = enc.segment();
+ }
+
+ synchronized (sendlock)
+ {
+ fragment(flags, type, method, methodSeg);
+ if (payload)
+ {
+ fragment((byte) 0x0, SegmentType.HEADER, method, headerSeg);
+ fragment(LAST_SEG, SegmentType.BODY, method, method.getBody());
+ }
+ }
+ }
+
+ public void error(Void v, ProtocolError error)
+ {
+ throw new IllegalArgumentException("" + error);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Frame.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Frame.java
new file mode 100644
index 0000000000..849355276e
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Frame.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.transport.network;
+
+import org.apache.qpid.transport.SegmentType;
+import org.apache.qpid.transport.util.SliceIterator;
+
+import java.nio.ByteBuffer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+
+/**
+ * Frame
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class Frame implements NetworkEvent
+{
+ public static final int HEADER_SIZE = 12;
+
+ // XXX: enums?
+ public static final byte L1 = 0;
+ public static final byte L2 = 1;
+ public static final byte L3 = 2;
+ public static final byte L4 = 3;
+
+ public static final byte RESERVED = 0x0;
+
+ public static final byte VERSION = 0x0;
+
+ public static final byte FIRST_SEG = 0x8;
+ public static final byte LAST_SEG = 0x4;
+ public static final byte FIRST_FRAME = 0x2;
+ public static final byte LAST_FRAME = 0x1;
+
+ final private byte flags;
+ final private SegmentType type;
+ final private byte track;
+ final private int channel;
+ final private ByteBuffer body;
+
+ public Frame(byte flags, SegmentType type, byte track, int channel,
+ ByteBuffer body)
+ {
+ this.flags = flags;
+ this.type = type;
+ this.track = track;
+ this.channel = channel;
+ this.body = body;
+ }
+
+ public ByteBuffer getBody()
+ {
+ return body.slice();
+ }
+
+ public byte getFlags()
+ {
+ return flags;
+ }
+
+ public int getChannel()
+ {
+ return channel;
+ }
+
+ public int getSize()
+ {
+ return body.remaining();
+ }
+
+ public SegmentType getType()
+ {
+ return type;
+ }
+
+ public byte getTrack()
+ {
+ return track;
+ }
+
+ private boolean flag(byte mask)
+ {
+ return (flags & mask) != 0;
+ }
+
+ public boolean isFirstSegment()
+ {
+ return flag(FIRST_SEG);
+ }
+
+ public boolean isLastSegment()
+ {
+ return flag(LAST_SEG);
+ }
+
+ public boolean isFirstFrame()
+ {
+ return flag(FIRST_FRAME);
+ }
+
+ public boolean isLastFrame()
+ {
+ return flag(LAST_FRAME);
+ }
+
+ public void delegate(NetworkDelegate delegate)
+ {
+ delegate.frame(this);
+ }
+
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+
+ str.append(String.format
+ ("[%05d %05d %1d %s %d%d%d%d] ", getChannel(), getSize(),
+ getTrack(), getType(),
+ isFirstSegment() ? 1 : 0, isLastSegment() ? 1 : 0,
+ isFirstFrame() ? 1 : 0, isLastFrame() ? 1 : 0));
+
+ str.append(str(body));
+
+ return str.toString();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java
new file mode 100644
index 0000000000..408c95e075
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java
@@ -0,0 +1,204 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.qpid.transport.ProtocolError;
+import org.apache.qpid.transport.ProtocolHeader;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.SegmentType;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+import static org.apache.qpid.transport.network.InputHandler.State.*;
+
+
+/**
+ * InputHandler
+ *
+ * @author Rafael H. Schloming
+ */
+
+public final class InputHandler implements Receiver<ByteBuffer>
+{
+
+ public enum State
+ {
+ PROTO_HDR,
+ FRAME_HDR,
+ FRAME_BODY,
+ ERROR;
+ }
+
+ private final Receiver<NetworkEvent> receiver;
+ private State state;
+ private ByteBuffer input = null;
+ private int needed;
+
+ private byte flags;
+ private SegmentType type;
+ private byte track;
+ private int channel;
+
+ public InputHandler(Receiver<NetworkEvent> receiver, State state)
+ {
+ this.receiver = receiver;
+ this.state = state;
+
+ switch (state)
+ {
+ case PROTO_HDR:
+ needed = 8;
+ break;
+ case FRAME_HDR:
+ needed = Frame.HEADER_SIZE;
+ break;
+ }
+ }
+
+ public InputHandler(Receiver<NetworkEvent> receiver)
+ {
+ this(receiver, PROTO_HDR);
+ }
+
+ private void error(String fmt, Object ... args)
+ {
+ receiver.received(new ProtocolError(Frame.L1, fmt, args));
+ }
+
+ public void received(ByteBuffer buf)
+ {
+ int limit = buf.limit();
+ int remaining = buf.remaining();
+ while (remaining > 0)
+ {
+ if (remaining >= needed)
+ {
+ int consumed = needed;
+ int pos = buf.position();
+ if (input == null)
+ {
+ buf.limit(pos + needed);
+ input = buf;
+ state = next(pos);
+ buf.limit(limit);
+ buf.position(pos + consumed);
+ }
+ else
+ {
+ buf.limit(pos + needed);
+ input.put(buf);
+ buf.limit(limit);
+ input.flip();
+ state = next(0);
+ }
+
+ remaining -= consumed;
+ input = null;
+ }
+ else
+ {
+ if (input == null)
+ {
+ input = ByteBuffer.allocate(needed);
+ }
+ input.put(buf);
+ needed -= remaining;
+ remaining = 0;
+ }
+ }
+ }
+
+ private State next(int pos)
+ {
+ input.order(ByteOrder.BIG_ENDIAN);
+
+ switch (state) {
+ case PROTO_HDR:
+ if (input.get(pos) != 'A' &&
+ input.get(pos + 1) != 'M' &&
+ input.get(pos + 2) != 'Q' &&
+ input.get(pos + 3) != 'P')
+ {
+ error("bad protocol header: %s", str(input));
+ return ERROR;
+ }
+
+ byte instance = input.get(pos + 5);
+ byte major = input.get(pos + 6);
+ byte minor = input.get(pos + 7);
+ receiver.received(new ProtocolHeader(instance, major, minor));
+ needed = Frame.HEADER_SIZE;
+ return FRAME_HDR;
+ case FRAME_HDR:
+ flags = input.get(pos);
+ type = SegmentType.get(input.get(pos + 1));
+ int size = (0xFFFF & input.getShort(pos + 2));
+ size -= Frame.HEADER_SIZE;
+ if (size < 0 || size > (64*1024 - 12))
+ {
+ error("bad frame size: %d", size);
+ return ERROR;
+ }
+ byte b = input.get(pos + 5);
+ if ((b & 0xF0) != 0) {
+ error("non-zero reserved bits in upper nibble of " +
+ "frame header byte 5: '%x'", b);
+ return ERROR;
+ } else {
+ track = (byte) (b & 0xF);
+ }
+ channel = (0xFFFF & input.getShort(pos + 6));
+ if (size == 0)
+ {
+ Frame frame = new Frame(flags, type, track, channel, ByteBuffer.allocate(0));
+ receiver.received(frame);
+ needed = Frame.HEADER_SIZE;
+ return FRAME_HDR;
+ }
+ else
+ {
+ needed = size;
+ return FRAME_BODY;
+ }
+ case FRAME_BODY:
+ Frame frame = new Frame(flags, type, track, channel, input.slice());
+ receiver.received(frame);
+ needed = Frame.HEADER_SIZE;
+ return FRAME_HDR;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public void exception(Throwable t)
+ {
+ receiver.exception(t);
+ }
+
+ public void closed()
+ {
+ receiver.closed();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java
new file mode 100644
index 0000000000..fbdfe6e84c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network;
+
+import org.apache.qpid.transport.ProtocolError;
+import org.apache.qpid.transport.ProtocolHeader;
+
+
+/**
+ * NetworkDelegate
+ *
+ * @author Rafael H. Schloming
+ */
+
+public interface NetworkDelegate
+{
+
+ void init(ProtocolHeader header);
+
+ void frame(Frame frame);
+
+ void error(ProtocolError error);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java
new file mode 100644
index 0000000000..91314cd4ad
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network;
+
+
+/**
+ * NetworkEvent
+ *
+ */
+
+public interface NetworkEvent
+{
+
+ void delegate(NetworkDelegate delegate);
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java
new file mode 100644
index 0000000000..ecc5f6d07c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java
@@ -0,0 +1,130 @@
+package org.apache.qpid.transport.network.io;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.nio.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.AMQMethodBodyFactory;
+import org.apache.qpid.framing.BodyFactory;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentBodyFactory;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentHeaderBodyFactory;
+import org.apache.qpid.framing.HeartbeatBody;
+import org.apache.qpid.framing.HeartbeatBodyFactory;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.transport.Receiver;
+
+public class InputHandler_0_9 implements Receiver<ByteBuffer>
+{
+
+ private AMQVersionAwareProtocolSession _session;
+ private MethodRegistry _registry;
+ private BodyFactory bodyFactory;
+ 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();
+ }
+
+ public InputHandler_0_9(AMQVersionAwareProtocolSession session)
+ {
+ _session = session;
+ _registry = _session.getMethodRegistry();
+ }
+
+ public void closed()
+ {
+ // AS FIXME: implement
+ }
+
+ public void exception(Throwable t)
+ {
+ // TODO: propogate exception to things
+ t.printStackTrace();
+ }
+
+ public void received(ByteBuffer buf)
+ {
+ org.apache.mina.common.ByteBuffer in = org.apache.mina.common.ByteBuffer.wrap(buf);
+ try
+ {
+ final byte type = in.get();
+ if (type == AMQMethodBody.TYPE)
+ {
+ bodyFactory = new AMQMethodBodyFactory(_session);
+ }
+ 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);
+ }
+
+ try
+ {
+ frame.getBodyFrame().handle(frame.getChannel(), _session);
+ }
+ catch (AMQException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java
new file mode 100644
index 0000000000..8530240dcc
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network.io;
+
+import org.apache.qpid.transport.Binding;
+import org.apache.qpid.transport.TransportException;
+
+import java.io.IOException;
+
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * IoAcceptor
+ *
+ */
+
+public class IoAcceptor<E> extends Thread
+{
+
+
+ private ServerSocket socket;
+ private Binding<E,ByteBuffer> binding;
+
+ public IoAcceptor(SocketAddress address, Binding<E,ByteBuffer> binding)
+ throws IOException
+ {
+ socket = new ServerSocket();
+ socket.setReuseAddress(true);
+ socket.bind(address);
+ this.binding = binding;
+
+ setName(String.format("IoAcceptor - %s", socket.getInetAddress()));
+ }
+
+ /**
+ Close the underlying ServerSocket if it has not already been closed.
+ */
+ public void close() throws IOException
+ {
+ if (!socket.isClosed())
+ {
+ socket.close();
+ }
+ }
+
+ public IoAcceptor(String host, int port, Binding<E,ByteBuffer> binding)
+ throws IOException
+ {
+ this(new InetSocketAddress(host, port), binding);
+ }
+
+ public void run()
+ {
+ while (true)
+ {
+ try
+ {
+ Socket sock = socket.accept();
+ IoTransport<E> transport = new IoTransport<E>(sock, binding,false);
+ }
+ catch (IOException e)
+ {
+ throw new TransportException(e);
+ }
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java
new file mode 100644
index 0000000000..351d8d24e8
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java
@@ -0,0 +1,145 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network.io;
+
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.TransportException;
+import org.apache.qpid.transport.util.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * IoReceiver
+ *
+ */
+
+final class IoReceiver extends Thread
+{
+
+ private static final Logger log = Logger.get(IoReceiver.class);
+
+ private final IoTransport transport;
+ private final Receiver<ByteBuffer> receiver;
+ private final int bufferSize;
+ private final Socket socket;
+ private final long timeout;
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+ private final boolean shutdownBroken =
+ ((String) System.getProperties().get("os.name")).matches("(?i).*windows.*");
+
+ public IoReceiver(IoTransport transport, Receiver<ByteBuffer> receiver,
+ int bufferSize, long timeout)
+ {
+ this.transport = transport;
+ this.receiver = receiver;
+ this.bufferSize = bufferSize;
+ this.socket = transport.getSocket();
+ this.timeout = timeout;
+
+ setDaemon(true);
+ setName(String.format("IoReceiver - %s", socket.getRemoteSocketAddress()));
+ start();
+ }
+
+ void close(boolean block)
+ {
+ if (!closed.getAndSet(true))
+ {
+ try
+ {
+ if (shutdownBroken)
+ {
+ socket.close();
+ }
+ else
+ {
+ socket.shutdownInput();
+ }
+ if (block && Thread.currentThread() != this)
+ {
+ join(timeout);
+ if (isAlive())
+ {
+ throw new TransportException("join timed out");
+ }
+ }
+ }
+ catch (InterruptedException e)
+ {
+ throw new TransportException(e);
+ }
+ catch (IOException e)
+ {
+ throw new TransportException(e);
+ }
+ }
+ }
+
+ public void run()
+ {
+ final int threshold = bufferSize / 2;
+
+ // I set the read buffer size simillar to SO_RCVBUF
+ // Haven't tested with a lower value to see if it's better or worse
+ byte[] buffer = new byte[bufferSize];
+ try
+ {
+ InputStream in = socket.getInputStream();
+ int read = 0;
+ int offset = 0;
+ while ((read = in.read(buffer, offset, bufferSize-offset)) != -1)
+ {
+ if (read > 0)
+ {
+ ByteBuffer b = ByteBuffer.wrap(buffer,offset,read);
+ receiver.received(b);
+ offset+=read;
+ if (offset > threshold)
+ {
+ offset = 0;
+ buffer = new byte[bufferSize];
+ }
+ }
+ }
+ socket.close();
+ }
+ catch (Throwable t)
+ {
+ if (!(shutdownBroken &&
+ t instanceof SocketException &&
+ t.getMessage().equalsIgnoreCase("socket closed") &&
+ closed.get()))
+ {
+ receiver.exception(t);
+ }
+ }
+ finally
+ {
+ receiver.closed();
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java
new file mode 100644
index 0000000000..36ea14856a
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.qpid.transport.network.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.SenderException;
+import org.apache.qpid.transport.TransportException;
+import org.apache.qpid.transport.util.Logger;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+
+public final class IoSender extends Thread implements Sender<ByteBuffer>
+{
+
+ private static final Logger log = Logger.get(IoSender.class);
+
+ // by starting here, we ensure that we always test the wraparound
+ // case, we should probably make this configurable somehow so that
+ // we can test other cases as well
+ private final static int START = Integer.MAX_VALUE - 10;
+
+ private final IoTransport transport;
+ private final long timeout;
+ private final Socket socket;
+ private final OutputStream out;
+
+ private final byte[] buffer;
+ private volatile int head = START;
+ private volatile int tail = START;
+ private volatile boolean idle = true;
+ private final Object notFull = new Object();
+ private final Object notEmpty = new Object();
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+
+ private volatile Throwable exception = null;
+
+
+ public IoSender(IoTransport transport, int bufferSize, long timeout)
+ {
+ this.transport = transport;
+ this.socket = transport.getSocket();
+ this.buffer = new byte[pof2(bufferSize)]; // buffer size must be a power of 2
+ this.timeout = timeout;
+
+ try
+ {
+ out = socket.getOutputStream();
+ }
+ catch (IOException e)
+ {
+ throw new TransportException("Error getting output stream for socket", e);
+ }
+
+ setDaemon(true);
+ setName(String.format("IoSender - %s", socket.getRemoteSocketAddress()));
+ start();
+ }
+
+ private static final int pof2(int n)
+ {
+ int result = 1;
+ while (result < n)
+ {
+ result *= 2;
+ }
+ return result;
+ }
+
+ public void send(ByteBuffer buf)
+ {
+ if (closed.get())
+ {
+ throw new SenderException("sender is closed", exception);
+ }
+
+ final int size = buffer.length;
+ int remaining = buf.remaining();
+
+ while (remaining > 0)
+ {
+ final int hd = head;
+ final int tl = tail;
+
+ if (hd - tl >= size)
+ {
+ flush();
+ synchronized (notFull)
+ {
+ long start = System.currentTimeMillis();
+ long elapsed = 0;
+ while (!closed.get() && head - tail >= size && elapsed < timeout)
+ {
+ try
+ {
+ notFull.wait(timeout - elapsed);
+ }
+ catch (InterruptedException e)
+ {
+ // pass
+ }
+ elapsed += System.currentTimeMillis() - start;
+ }
+
+ if (closed.get())
+ {
+ throw new SenderException("sender is closed", exception);
+ }
+
+ if (head - tail >= size)
+ {
+ throw new SenderException(String.format("write timed out: %s, %s", head, tail));
+ }
+ }
+ continue;
+ }
+
+ final int hd_idx = mod(hd, size);
+ final int tl_idx = mod(tl, size);
+ final int length;
+
+ if (tl_idx > hd_idx)
+ {
+ length = Math.min(tl_idx - hd_idx, remaining);
+ }
+ else
+ {
+ length = Math.min(size - hd_idx, remaining);
+ }
+
+ buf.get(buffer, hd_idx, length);
+ head += length;
+ remaining -= length;
+ }
+ }
+
+ public void flush()
+ {
+ if (idle)
+ {
+ synchronized (notEmpty)
+ {
+ notEmpty.notify();
+ }
+ }
+ }
+
+ public void close()
+ {
+ close(true);
+ }
+
+ void close(boolean reportException)
+ {
+ if (!closed.getAndSet(true))
+ {
+ synchronized (notFull)
+ {
+ notFull.notify();
+ }
+
+ synchronized (notEmpty)
+ {
+ notEmpty.notify();
+ }
+
+ try
+ {
+ if (Thread.currentThread() != this)
+ {
+ join(timeout);
+ if (isAlive())
+ {
+ throw new SenderException("join timed out");
+ }
+ }
+ transport.getReceiver().close(false);
+ }
+ catch (InterruptedException e)
+ {
+ throw new SenderException(e);
+ }
+
+ if (reportException && exception != null)
+ {
+ throw new SenderException(exception);
+ }
+ }
+ }
+
+ public void run()
+ {
+ final int size = buffer.length;
+
+ while (true)
+ {
+ final int hd = head;
+ final int tl = tail;
+
+ if (hd == tl)
+ {
+ if (closed.get())
+ {
+ break;
+ }
+
+ idle = true;
+
+ synchronized (notEmpty)
+ {
+ while (head == tail && !closed.get())
+ {
+ try
+ {
+ notEmpty.wait();
+ }
+ catch (InterruptedException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ idle = false;
+
+ continue;
+ }
+
+ final int hd_idx = mod(hd, size);
+ final int tl_idx = mod(tl, size);
+
+ final int length;
+ if (tl_idx < hd_idx)
+ {
+ length = hd_idx - tl_idx;
+ }
+ else
+ {
+ length = size - tl_idx;
+ }
+
+ try
+ {
+ out.write(buffer, tl_idx, length);
+ }
+ catch (IOException e)
+ {
+ log.error(e, "error in write thread");
+ exception = e;
+ close(false);
+ break;
+ }
+ tail += length;
+ if (head - tl >= size)
+ {
+ synchronized (notFull)
+ {
+ notFull.notify();
+ }
+ }
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java
new file mode 100644
index 0000000000..3615461e9f
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.transport.network.io;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.ssl.SSLContextFactory;
+import org.apache.qpid.transport.Binding;
+import org.apache.qpid.transport.Connection;
+import org.apache.qpid.transport.ConnectionDelegate;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.TransportException;
+import org.apache.qpid.transport.network.ConnectionBinding;
+import org.apache.qpid.transport.network.ssl.SSLReceiver;
+import org.apache.qpid.transport.network.ssl.SSLSender;
+import org.apache.qpid.transport.util.Logger;
+
+/**
+ * This class provides a socket based transport using the java.io
+ * classes.
+ *
+ * The following params are configurable via JVM arguments
+ * TCP_NO_DELAY - amqj.tcpNoDelay
+ * SO_RCVBUF - amqj.receiveBufferSize
+ * SO_SNDBUF - amqj.sendBufferSize
+ */
+public final class IoTransport<E>
+{
+
+ static
+ {
+ org.apache.mina.common.ByteBuffer.setAllocator
+ (new org.apache.mina.common.SimpleByteBufferAllocator());
+ org.apache.mina.common.ByteBuffer.setUseDirectBuffers
+ (Boolean.getBoolean("amqj.enableDirectBuffers"));
+ }
+
+ private static final Logger log = Logger.get(IoTransport.class);
+
+ private static int DEFAULT_READ_WRITE_BUFFER_SIZE = 64 * 1024;
+ private static int readBufferSize = Integer.getInteger
+ ("amqj.receiveBufferSize", DEFAULT_READ_WRITE_BUFFER_SIZE);
+ private static int writeBufferSize = Integer.getInteger
+ ("amqj.sendBufferSize", DEFAULT_READ_WRITE_BUFFER_SIZE);
+
+ private Socket socket;
+ private Sender<ByteBuffer> sender;
+ private E endpoint;
+ private IoReceiver receiver;
+ private long timeout = 60000;
+
+ IoTransport(Socket socket, Binding<E,ByteBuffer> binding, boolean ssl)
+ {
+ this.socket = socket;
+
+ if (ssl)
+ {
+ SSLEngine engine = null;
+ SSLContext sslCtx;
+ try
+ {
+ sslCtx = createSSLContext();
+ }
+ catch (Exception e)
+ {
+ throw new TransportException("Error creating SSL Context", e);
+ }
+
+ try
+ {
+ engine = sslCtx.createSSLEngine();
+ engine.setUseClientMode(true);
+ }
+ catch(Exception e)
+ {
+ throw new TransportException("Error creating SSL Engine", e);
+ }
+
+ this.sender = new SSLSender(engine,new IoSender(this, 2*writeBufferSize, timeout));
+ this.endpoint = binding.endpoint(sender);
+ this.receiver = new IoReceiver(this, new SSLReceiver(engine,binding.receiver(endpoint),(SSLSender)sender),
+ 2*readBufferSize, timeout);
+ }
+ else
+ {
+ this.sender = new IoSender(this, 2*writeBufferSize, timeout);
+ this.endpoint = binding.endpoint(sender);
+ this.receiver = new IoReceiver(this, binding.receiver(endpoint),
+ 2*readBufferSize, timeout);
+ }
+ }
+
+ Sender<ByteBuffer> getSender()
+ {
+ return sender;
+ }
+
+ IoReceiver getReceiver()
+ {
+ return receiver;
+ }
+
+ Socket getSocket()
+ {
+ return socket;
+ }
+
+ public static final <E> E connect(String host, int port,
+ Binding<E,ByteBuffer> binding,
+ boolean ssl)
+ {
+ Socket socket = createSocket(host, port);
+ IoTransport<E> transport = new IoTransport<E>(socket, binding,ssl);
+ return transport.endpoint;
+ }
+
+ public static final Connection connect(String host, int port,
+ ConnectionDelegate delegate,
+ boolean ssl)
+ {
+ return connect(host, port, ConnectionBinding.get(delegate),ssl);
+ }
+
+ public static void connect_0_9(AMQVersionAwareProtocolSession session, String host, int port, boolean ssl)
+ {
+ connect(host, port, new Binding_0_9(session),ssl);
+ }
+
+ private static class Binding_0_9
+ implements Binding<AMQVersionAwareProtocolSession,ByteBuffer>
+ {
+
+ private AMQVersionAwareProtocolSession session;
+
+ Binding_0_9(AMQVersionAwareProtocolSession session)
+ {
+ this.session = session;
+ }
+
+ public AMQVersionAwareProtocolSession endpoint(Sender<ByteBuffer> sender)
+ {
+ session.setSender(sender);
+ return session;
+ }
+
+ public Receiver<ByteBuffer> receiver(AMQVersionAwareProtocolSession ssn)
+ {
+ return new InputHandler_0_9(ssn);
+ }
+
+ }
+
+ private static Socket createSocket(String host, int port)
+ {
+ try
+ {
+ InetAddress address = InetAddress.getByName(host);
+ Socket socket = new Socket();
+ socket.setReuseAddress(true);
+ socket.setTcpNoDelay(Boolean.getBoolean("amqj.tcpNoDelay"));
+
+ log.debug("default-SO_RCVBUF : %s", socket.getReceiveBufferSize());
+ log.debug("default-SO_SNDBUF : %s", socket.getSendBufferSize());
+
+ socket.setSendBufferSize(writeBufferSize);
+ socket.setReceiveBufferSize(readBufferSize);
+
+ log.debug("new-SO_RCVBUF : %s", socket.getReceiveBufferSize());
+ log.debug("new-SO_SNDBUF : %s", socket.getSendBufferSize());
+
+ socket.connect(new InetSocketAddress(address, port));
+ return socket;
+ }
+ catch (SocketException e)
+ {
+ throw new TransportException("Error connecting to broker", e);
+ }
+ catch (IOException e)
+ {
+ throw new TransportException("Error connecting to broker", e);
+ }
+ }
+
+ private SSLContext createSSLContext() throws Exception
+ {
+ String trustStorePath = System.getProperty("javax.net.ssl.trustStore");
+ String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+ String trustStoreCertType = System.getProperty("qpid.ssl.trustStoreCertType","SunX509");
+
+ String keyStorePath = System.getProperty("javax.net.ssl.keyStore",trustStorePath);
+ String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword",trustStorePassword);
+ String keyStoreCertType = System.getProperty("qpid.ssl.keyStoreCertType","SunX509");
+
+ SSLContextFactory sslContextFactory = new SSLContextFactory(trustStorePath,trustStorePassword,
+ trustStoreCertType,keyStorePath,
+ keyStorePassword,keyStoreCertType);
+
+ return sslContextFactory.buildServerContext();
+
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java
new file mode 100644
index 0000000000..b89eed48b0
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java
@@ -0,0 +1,274 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network.mina;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.apache.mina.common.*;
+
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.apache.mina.transport.socket.nio.SocketConnector;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.filter.executor.ExecutorFilter;
+
+import org.apache.qpid.transport.Binding;
+import org.apache.qpid.transport.Connection;
+import org.apache.qpid.transport.ConnectionDelegate;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.network.ConnectionBinding;
+
+import org.apache.qpid.transport.util.Logger;
+
+import org.apache.qpid.transport.network.Assembler;
+import org.apache.qpid.transport.network.Disassembler;
+import org.apache.qpid.transport.network.InputHandler;
+
+import static org.apache.qpid.transport.util.Functions.*;
+
+/**
+ * MinaHandler
+ *
+ * @author Rafael H. Schloming
+ */
+//RA making this public until we sort out the package issues
+public class MinaHandler<E> implements IoHandler
+{
+ /** Default buffer size for pending messages reads */
+ private static final String DEFAULT_READ_BUFFER_LIMIT = "262144";
+ /** Default buffer size for pending messages writes */
+ private static final String DEFAULT_WRITE_BUFFER_LIMIT = "262144";
+ private static final int MAX_RCVBUF = 64*1024;
+
+ private static final Logger log = Logger.get(MinaHandler.class);
+
+ static
+ {
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+ ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers"));
+ }
+
+ private final Binding<E,java.nio.ByteBuffer> binding;
+
+ private MinaHandler(Binding<E,java.nio.ByteBuffer> binding)
+ {
+ this.binding = binding;
+ }
+
+ public void messageReceived(IoSession ssn, Object obj)
+ {
+ Attachment<E> attachment = (Attachment<E>) ssn.getAttachment();
+ ByteBuffer buf = (ByteBuffer) obj;
+ try
+ {
+ attachment.receiver.received(buf.buf());
+ }
+ catch (Throwable t)
+ {
+ log.error(t, "exception handling buffer %s", str(buf.buf()));
+ throw new RuntimeException(t);
+ }
+ }
+
+ public void messageSent(IoSession ssn, Object obj)
+ {
+ // do nothing
+ }
+
+ public void exceptionCaught(IoSession ssn, Throwable e)
+ {
+ Attachment<E> attachment = (Attachment<E>) ssn.getAttachment();
+ attachment.receiver.exception(e);
+ }
+
+ /**
+ * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the
+ * session, which filters the events handled by this handler. The filter chain consists of, handing off events
+ * to an optional protectio
+ *
+ * @param session The MINA session.
+ * @throws Exception Any underlying exceptions are allowed to fall through to MINA.
+ */
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ log.debug("Protocol session created for session " + System.identityHashCode(session));
+
+ if (Boolean.getBoolean("protectio"))
+ {
+ try
+ {
+ //Add IO Protection Filters
+ IoFilterChain chain = session.getFilterChain();
+
+ session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter());
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(
+ Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER_LIMIT)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+ writefilter.setMaximumConnectionBufferSize(
+ Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER_LIMIT)));
+ writefilter.attach(chain);
+ session.getFilterChain().remove("tempExecutorFilterForFilterBuilder");
+
+ log.info("Using IO Read/Write Filter Protection");
+ }
+ catch (Exception e)
+ {
+ log.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage());
+ }
+ }
+ }
+
+ public void sessionOpened(final IoSession ssn)
+ {
+ log.debug("opened: %s", this);
+ E endpoint = binding.endpoint(new MinaSender(ssn));
+ Attachment<E> attachment =
+ new Attachment<E>(endpoint, binding.receiver(endpoint));
+
+ // We need to synchronize and notify here because the MINA
+ // connect future returns the session prior to the attachment
+ // being set. This is arguably a bug in MINA.
+ synchronized (ssn)
+ {
+ ssn.setAttachment(attachment);
+ ssn.notifyAll();
+ }
+ }
+
+ public void sessionClosed(IoSession ssn)
+ {
+ log.debug("closed: %s", ssn);
+ Attachment<E> attachment = (Attachment<E>) ssn.getAttachment();
+ attachment.receiver.closed();
+ ssn.setAttachment(null);
+ }
+
+ public void sessionIdle(IoSession ssn, IdleStatus status)
+ {
+ // do nothing
+ }
+
+ private static class Attachment<E>
+ {
+
+ E endpoint;
+ Receiver<java.nio.ByteBuffer> receiver;
+
+ Attachment(E endpoint, Receiver<java.nio.ByteBuffer> receiver)
+ {
+ this.endpoint = endpoint;
+ this.receiver = receiver;
+ }
+ }
+
+ public static final void accept(String host, int port,
+ Binding<?,java.nio.ByteBuffer> binding)
+ throws IOException
+ {
+ accept(new InetSocketAddress(host, port), binding);
+ }
+
+ public static final <E> void accept(SocketAddress address,
+ Binding<E,java.nio.ByteBuffer> binding)
+ throws IOException
+ {
+ IoAcceptor acceptor = new SocketAcceptor();
+ acceptor.bind(address, new MinaHandler<E>(binding));
+ }
+
+ public static final <E> E connect(String host, int port,
+ Binding<E,java.nio.ByteBuffer> binding)
+ {
+ return connect(new InetSocketAddress(host, port), binding);
+ }
+
+ public static final <E> E connect(SocketAddress address,
+ Binding<E,java.nio.ByteBuffer> binding)
+ {
+ MinaHandler<E> handler = new MinaHandler<E>(binding);
+ SocketConnector connector = new SocketConnector();
+ IoServiceConfig acceptorConfig = connector.getDefaultConfig();
+ acceptorConfig.setThreadModel(ThreadModel.MANUAL);
+ SocketSessionConfig scfg = (SocketSessionConfig) acceptorConfig.getSessionConfig();
+ scfg.setTcpNoDelay(Boolean.getBoolean("amqj.tcpNoDelay"));
+ Integer sendBufferSize = Integer.getInteger("amqj.sendBufferSize");
+ if (sendBufferSize != null && sendBufferSize > 0)
+ {
+ scfg.setSendBufferSize(sendBufferSize);
+ }
+ Integer receiveBufferSize = Integer.getInteger("amqj.receiveBufferSize");
+ if (receiveBufferSize != null && receiveBufferSize > 0)
+ {
+ scfg.setReceiveBufferSize(receiveBufferSize);
+ }
+ else if (scfg.getReceiveBufferSize() > MAX_RCVBUF)
+ {
+ scfg.setReceiveBufferSize(MAX_RCVBUF);
+ }
+ connector.setWorkerTimeout(0);
+ ConnectFuture cf = connector.connect(address, handler);
+ cf.join();
+ IoSession ssn = cf.getSession();
+
+ // We need to synchronize and wait here because the MINA
+ // connect future returns the session prior to the attachment
+ // being set. This is arguably a bug in MINA.
+ synchronized (ssn)
+ {
+ while (ssn.getAttachment() == null)
+ {
+ try
+ {
+ ssn.wait();
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ Attachment<E> attachment = (Attachment<E>) ssn.getAttachment();
+ return attachment.endpoint;
+ }
+
+ public static final void accept(String host, int port,
+ ConnectionDelegate delegate)
+ throws IOException
+ {
+ accept(host, port, ConnectionBinding.get(delegate));
+ }
+
+ public static final Connection connect(String host, int port,
+ ConnectionDelegate delegate)
+ {
+ return connect(host, port, ConnectionBinding.get(delegate));
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java
new file mode 100644
index 0000000000..69d4061e0c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.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.transport.network.mina;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.CloseFuture;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.TransportException;
+
+
+/**
+ * MinaSender
+ */
+
+public class MinaSender implements Sender<java.nio.ByteBuffer>
+{
+ private static final int TIMEOUT = 2 * 60 * 1000;
+
+ private final IoSession session;
+ private WriteFuture lastWrite = null;
+
+ public MinaSender(IoSession session)
+ {
+ this.session = session;
+ }
+
+ public void send(java.nio.ByteBuffer buf)
+ {
+ if (session.isClosing())
+ {
+ throw new TransportException("attempted to write to a closed socket");
+ }
+
+ synchronized (this)
+ {
+ lastWrite = session.write(ByteBuffer.wrap(buf));
+ }
+ }
+
+ public void flush()
+ {
+ // pass
+ }
+
+ public synchronized void close()
+ {
+ // MINA will sometimes throw away in-progress writes when you
+ // ask it to close
+ synchronized (this)
+ {
+ if (lastWrite != null)
+ {
+ lastWrite.join();
+ }
+ }
+ CloseFuture closed = session.close();
+ closed.join();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java
new file mode 100644
index 0000000000..3bc6730623
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java
@@ -0,0 +1,140 @@
+package org.apache.qpid.transport.network.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.
+ *
+ */
+
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.qpid.transport.Connection;
+import org.apache.qpid.transport.ConnectionDelegate;
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.network.Assembler;
+import org.apache.qpid.transport.network.Disassembler;
+import org.apache.qpid.transport.network.InputHandler;
+
+public class NioHandler implements Runnable
+{
+ private Receiver<ByteBuffer> _receiver;
+ private SocketChannel _ch;
+ private ByteBuffer _readBuf;
+ private static Map<Integer,NioSender> _handlers = new ConcurrentHashMap<Integer,NioSender>();
+ private AtomicInteger _count = new AtomicInteger();
+
+ private NioHandler(){}
+
+ public static final Connection connect(String host, int port,
+ ConnectionDelegate delegate)
+ {
+ NioHandler handler = new NioHandler();
+ return handler.connectInternal(host,port,delegate);
+ }
+
+ private Connection connectInternal(String host, int port,
+ ConnectionDelegate delegate)
+ {
+ try
+ {
+ SocketAddress address = new InetSocketAddress(host,port);
+ _ch = SocketChannel.open();
+ _ch.socket().setReuseAddress(true);
+ _ch.configureBlocking(true);
+ _ch.socket().setTcpNoDelay(true);
+ if (address != null)
+ {
+ _ch.socket().connect(address);
+ }
+ while (_ch.isConnectionPending())
+ {
+
+ }
+
+ }
+ catch (SocketException e)
+ {
+
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ NioSender sender = new NioSender(_ch);
+ Connection con = new Connection();
+ con.setSender(new Disassembler(sender, 64*1024 - 1));
+ con.setConnectionDelegate(delegate);
+
+ con.setConnectionId(_count.incrementAndGet());
+ _handlers.put(con.getConnectionId(),sender);
+
+ _receiver = new InputHandler(new Assembler(con), InputHandler.State.FRAME_HDR);
+
+ Thread t = new Thread(this);
+ t.start();
+
+ return con;
+ }
+
+ public void run()
+ {
+ _readBuf = ByteBuffer.allocate(512);
+ long read = 0;
+ while(_ch.isConnected() && _ch.isOpen())
+ {
+ try
+ {
+ read = _ch.read(_readBuf);
+ if (read > 0)
+ {
+ _readBuf.flip();
+ ByteBuffer b = ByteBuffer.allocate(_readBuf.remaining());
+ b.put(_readBuf);
+ b.flip();
+ _readBuf.clear();
+ _receiver.received(b);
+ }
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ //throw new EOFException("The underlying socket/channel has closed");
+ }
+
+ public static void startBatchingFrames(int connectionId)
+ {
+ NioSender sender = _handlers.get(connectionId);
+ sender.setStartBatching();
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java
new file mode 100644
index 0000000000..8792fce142
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java
@@ -0,0 +1,121 @@
+package org.apache.qpid.transport.network.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.
+ *
+ */
+
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+
+import org.apache.qpid.transport.Sender;
+
+public class NioSender implements Sender<java.nio.ByteBuffer>
+{
+ private final Object lock = new Object();
+ private SocketChannel _ch;
+ private boolean _batch = false;
+ private ByteBuffer _batcher;
+
+ public NioSender(SocketChannel ch)
+ {
+ this._ch = ch;
+ }
+
+ public void send(java.nio.ByteBuffer buf)
+ {
+ if (_batch)
+ {
+ //System.out.println(_batcher.position() + " , " + buf.remaining() + " , " + buf.position() + ","+_batcher.capacity());
+ if (_batcher.position() + buf.remaining() >= _batcher.capacity())
+ {
+ _batcher.flip();
+ write(_batcher);
+ _batcher.clear();
+ if (buf.remaining() > _batcher.capacity())
+ {
+ write(buf);
+ }
+ else
+ {
+ _batcher.put(buf);
+ }
+ }
+ else
+ {
+ _batcher.put(buf);
+ }
+ }
+ else
+ {
+ write(buf);
+ }
+ }
+
+ public void flush()
+ {
+ // pass
+ }
+
+ private void write(java.nio.ByteBuffer buf)
+ {
+ synchronized (lock)
+ {
+ if( _ch.isConnected() && _ch.isOpen())
+ {
+ try
+ {
+ _ch.write(buf);
+ }
+ catch(Exception e)
+ {
+ e.fillInStackTrace();
+ }
+ }
+ else
+ {
+ throw new RuntimeException("Trying to write on a closed socket");
+ }
+
+ }
+ }
+
+ public void setStartBatching()
+ {
+ _batch = true;
+ _batcher = ByteBuffer.allocate(1024);
+ }
+
+ public void close()
+ {
+ // MINA will sometimes throw away in-progress writes when you
+ // ask it to close
+ synchronized (lock)
+ {
+ try
+ {
+ _ch.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java
new file mode 100644
index 0000000000..a9d2d50c51
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java
@@ -0,0 +1,179 @@
+/*
+*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network.ssl;
+
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+
+import org.apache.qpid.transport.Receiver;
+import org.apache.qpid.transport.TransportException;
+import org.apache.qpid.transport.util.Logger;
+
+public class SSLReceiver implements Receiver<ByteBuffer>
+{
+ private Receiver<ByteBuffer> delegate;
+ private SSLEngine engine;
+ private SSLSender sender;
+ private int sslBufSize;
+ private ByteBuffer appData;
+ private ByteBuffer localBuffer;
+ private boolean dataCached = false;
+ private final Object notificationToken;
+
+ private static final Logger log = Logger.get(SSLReceiver.class);
+
+ public SSLReceiver(SSLEngine engine, Receiver<ByteBuffer> delegate,SSLSender sender)
+ {
+ this.engine = engine;
+ this.delegate = delegate;
+ this.sender = sender;
+ this.sslBufSize = engine.getSession().getApplicationBufferSize();
+ appData = ByteBuffer.allocate(sslBufSize);
+ localBuffer = ByteBuffer.allocate(sslBufSize);
+ notificationToken = sender.getNotificationToken();
+ }
+
+ public void closed()
+ {
+ delegate.closed();
+ }
+
+ public void exception(Throwable t)
+ {
+ delegate.exception(t);
+ }
+
+ private ByteBuffer addPreviouslyUnreadData(ByteBuffer buf)
+ {
+ if (dataCached)
+ {
+ ByteBuffer b = ByteBuffer.allocate(localBuffer.remaining() + buf.remaining());
+ b.put(localBuffer);
+ b.put(buf);
+ b.flip();
+ dataCached = false;
+ return b;
+ }
+ else
+ {
+ return buf;
+ }
+ }
+
+ public void received(ByteBuffer buf)
+ {
+ ByteBuffer netData = addPreviouslyUnreadData(buf);
+
+ HandshakeStatus handshakeStatus;
+ Status status;
+
+ while (netData.hasRemaining())
+ {
+ try
+ {
+ SSLEngineResult result = engine.unwrap(netData, appData);
+ int read = result.bytesProduced();
+ status = result.getStatus();
+ handshakeStatus = result.getHandshakeStatus();
+
+ if (read > 0)
+ {
+ int limit = appData.limit();
+ appData.limit(appData.position());
+ appData.position(appData.position() - read);
+
+ ByteBuffer data = appData.slice();
+
+ appData.limit(limit);
+ appData.position(appData.position() + read);
+
+ delegate.received(data);
+ }
+
+
+ switch(status)
+ {
+ case CLOSED:
+ synchronized(notificationToken)
+ {
+ notificationToken.notifyAll();
+ }
+ return;
+
+ case BUFFER_OVERFLOW:
+ appData = ByteBuffer.allocate(sslBufSize);
+ continue;
+
+ case BUFFER_UNDERFLOW:
+ localBuffer.clear();
+ localBuffer.put(netData);
+ localBuffer.flip();
+ dataCached = true;
+ break;
+
+ case OK:
+ break; // do nothing
+
+ default:
+ throw new IllegalStateException("SSLReceiver: Invalid State " + status);
+ }
+
+ switch (handshakeStatus)
+ {
+ case NEED_UNWRAP:
+ if (netData.hasRemaining())
+ {
+ continue;
+ }
+ break;
+
+ case NEED_TASK:
+ sender.doTasks();
+ handshakeStatus = engine.getHandshakeStatus();
+
+ case NEED_WRAP:
+ case FINISHED:
+ case NOT_HANDSHAKING:
+ synchronized(notificationToken)
+ {
+ notificationToken.notifyAll();
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("SSLReceiver: Invalid State " + status);
+ }
+
+
+ }
+ catch(SSLException e)
+ {
+ throw new TransportException("Error in SSLReceiver",e);
+ }
+
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java
new file mode 100644
index 0000000000..5e37d5356b
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.network.ssl;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.SenderException;
+import org.apache.qpid.transport.util.Logger;
+
+public class SSLSender implements Sender<ByteBuffer>
+{
+ private Sender<ByteBuffer> delegate;
+ private SSLEngine engine;
+ private int sslBufSize;
+ private ByteBuffer netData;
+
+ private final Object engineState = new Object();
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+
+ private static final Logger log = Logger.get(SSLSender.class);
+
+ public SSLSender(SSLEngine engine, Sender<ByteBuffer> delegate)
+ {
+ this.engine = engine;
+ this.delegate = delegate;
+ sslBufSize = engine.getSession().getPacketBufferSize();
+ netData = ByteBuffer.allocate(sslBufSize);
+ }
+
+ public void close()
+ {
+ if (!closed.getAndSet(true))
+ {
+ if (engine.isOutboundDone())
+ {
+ return;
+ }
+ log.debug("Closing SSL connection");
+ engine.closeOutbound();
+ try
+ {
+ tearDownSSLConnection();
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException("Error closing SSL connection",e);
+ }
+
+ while (!engine.isOutboundDone())
+ {
+ synchronized(engineState)
+ {
+ try
+ {
+ engineState.wait();
+ }
+ catch(InterruptedException e)
+ {
+ // pass
+ }
+ }
+ }
+ delegate.close();
+ }
+ }
+
+ private void tearDownSSLConnection() throws Exception
+ {
+ SSLEngineResult result = engine.wrap(ByteBuffer.allocate(0), netData);
+ Status status = result.getStatus();
+ int read = result.bytesProduced();
+ while (status != Status.CLOSED)
+ {
+ if (status == Status.BUFFER_OVERFLOW)
+ {
+ netData.clear();
+ }
+ if(read > 0)
+ {
+ int limit = netData.limit();
+ netData.limit(netData.position());
+ netData.position(netData.position() - read);
+
+ ByteBuffer data = netData.slice();
+
+ netData.limit(limit);
+ netData.position(netData.position() + read);
+
+ delegate.send(data);
+ flush();
+ }
+ result = engine.wrap(ByteBuffer.allocate(0), netData);
+ status = result.getStatus();
+ read = result.bytesProduced();
+ }
+ }
+
+ public void flush()
+ {
+ delegate.flush();
+ }
+
+ public void send(ByteBuffer appData)
+ {
+ if (closed.get())
+ {
+ throw new SenderException("SSL Sender is closed");
+ }
+
+ HandshakeStatus handshakeStatus;
+ Status status;
+
+ while(appData.hasRemaining())
+ {
+
+ int read = 0;
+ try
+ {
+ SSLEngineResult result = engine.wrap(appData, netData);
+ read = result.bytesProduced();
+ status = result.getStatus();
+ handshakeStatus = result.getHandshakeStatus();
+
+ }
+ catch(SSLException e)
+ {
+ throw new SenderException("SSL, Error occurred while encrypting data",e);
+ }
+
+ if(read > 0)
+ {
+ int limit = netData.limit();
+ netData.limit(netData.position());
+ netData.position(netData.position() - read);
+
+ ByteBuffer data = netData.slice();
+
+ netData.limit(limit);
+ netData.position(netData.position() + read);
+
+ delegate.send(data);
+ }
+
+ switch(status)
+ {
+ case CLOSED:
+ throw new SenderException("SSLEngine is closed");
+
+ case BUFFER_OVERFLOW:
+ netData.clear();
+ continue;
+
+ case OK:
+ break; // do nothing
+
+ default:
+ throw new IllegalStateException("SSLReceiver: Invalid State " + status);
+ }
+
+ switch (handshakeStatus)
+ {
+ case NEED_WRAP:
+ if (netData.hasRemaining())
+ {
+ continue;
+ }
+
+ case NEED_TASK:
+ doTasks();
+ break;
+
+ case NEED_UNWRAP:
+ flush();
+ synchronized(engineState)
+ {
+ try
+ {
+ engineState.wait();
+ }
+ catch(InterruptedException e)
+ {
+ // pass
+ }
+ }
+ break;
+
+ case FINISHED:
+ case NOT_HANDSHAKING:
+ break; //do nothing
+
+ default:
+ throw new IllegalStateException("SSLReceiver: Invalid State " + status);
+ }
+
+ }
+ }
+
+ public void doTasks()
+ {
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ runnable.run();
+ }
+ }
+
+ public Object getNotificationToken()
+ {
+ return engineState;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java
new file mode 100644
index 0000000000..c220694b50
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.util;
+
+import java.nio.ByteBuffer;
+
+import static java.lang.Math.*;
+
+
+/**
+ * Functions
+ *
+ * @author Rafael H. Schloming
+ */
+
+public class Functions
+{
+
+ public static final int mod(int n, int m)
+ {
+ int r = n % m;
+ return r < 0 ? m + r : r;
+ }
+
+ public static final byte lsb(int i)
+ {
+ return (byte) (0xFF & i);
+ }
+
+ public static final byte lsb(long l)
+ {
+ return (byte) (0xFF & l);
+ }
+
+ public static final String str(ByteBuffer buf)
+ {
+ return str(buf, buf.remaining());
+ }
+
+ public static final String str(ByteBuffer buf, int limit)
+ {
+ StringBuilder str = new StringBuilder();
+ str.append('"');
+
+ for (int i = 0; i < min(buf.remaining(), limit); i++)
+ {
+ byte c = buf.get(buf.position() + i);
+
+ if (c > 31 && c < 127 && c != '\\')
+ {
+ str.append((char)c);
+ }
+ else
+ {
+ str.append(String.format("\\x%02x", c));
+ }
+ }
+
+ str.append('"');
+
+ if (limit < buf.remaining())
+ {
+ str.append("...");
+ }
+
+ return str.toString();
+ }
+
+ public static final String str(byte[] bytes)
+ {
+ return str(ByteBuffer.wrap(bytes));
+ }
+
+ public static final String str(byte[] bytes, int limit)
+ {
+ return str(ByteBuffer.wrap(bytes), limit);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java
new file mode 100644
index 0000000000..8c4818df92
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport.util;
+
+import org.slf4j.LoggerFactory;
+
+/**
+ * Logger
+ *
+ */
+
+public final class Logger
+{
+
+ public static final Logger get(Class<?> klass)
+ {
+ return new Logger(LoggerFactory.getLogger(klass));
+ }
+
+ private final org.slf4j.Logger log;
+
+ private Logger(org.slf4j.Logger log)
+ {
+ this.log = log;
+ }
+
+ public boolean isDebugEnabled()
+ {
+ return log.isDebugEnabled();
+ }
+
+ public void debug(String message, Object ... args)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug(String.format(message, args));
+ }
+ }
+
+ public void debug(Throwable t, String message, Object ... args)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug(String.format(message, args), t);
+ }
+ }
+
+ public void error(String message, Object ... args)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error(String.format(message, args));
+ }
+ }
+
+ public void error(Throwable t, String message, Object ... args)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error(String.format(message, args), t);
+ }
+ }
+
+ public void warn(String message, Object ... args)
+ {
+ if (log.isWarnEnabled())
+ {
+ log.warn(String.format(message, args));
+ }
+ }
+
+ public void warn(Throwable t, String message, Object ... args)
+ {
+ if (log.isWarnEnabled())
+ {
+ log.warn(String.format(message, args), t);
+ }
+ }
+
+ public void info(String message, Object ... args)
+ {
+ if (log.isInfoEnabled())
+ {
+ log.info(String.format(message, args));
+ }
+ }
+
+ public void info(Throwable t, String message, Object ... args)
+ {
+ if (log.isInfoEnabled())
+ {
+ log.info(String.format(message, args), t);
+ }
+ }
+
+ public void trace(String message, Object ... args)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace(String.format(message, args));
+ }
+ }
+
+ public void trace(Throwable t, String message, Object ... args)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace(String.format(message, args), t);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.java
new file mode 100644
index 0000000000..3db29847b2
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.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.transport.util;
+
+import java.nio.ByteBuffer;
+
+import java.util.Iterator;
+
+
+/**
+ * SliceIterator
+ *
+ * @author Rafael H. Schloming
+ */
+
+public class SliceIterator implements Iterator<ByteBuffer>
+{
+
+ final private Iterator<ByteBuffer> iterator;
+
+ public SliceIterator(Iterator<ByteBuffer> iterator)
+ {
+ this.iterator = iterator;
+ }
+
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ public ByteBuffer next()
+ {
+ return iterator.next().slice();
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java
new file mode 100644
index 0000000000..e034d779ca
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.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.transport.util;
+
+
+/**
+ * Waiter
+ *
+ */
+
+public final class Waiter
+{
+
+ private final Object lock;
+ private final long timeout;
+ private final long start;
+ private long elapsed;
+
+ public Waiter(Object lock, long timeout)
+ {
+ this.lock = lock;
+ this.timeout = timeout;
+ this.start = System.currentTimeMillis();
+ this.elapsed = 0;
+ }
+
+ public boolean hasTime()
+ {
+ return elapsed < timeout;
+ }
+
+ public void await()
+ {
+ try
+ {
+ lock.wait(timeout - elapsed);
+ }
+ catch (InterruptedException e)
+ {
+ // pass
+ }
+ elapsed = System.currentTimeMillis() - start;
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java
new file mode 100644
index 0000000000..998242925c
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.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.url;
+
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AMQBindingURL implements BindingURL
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQBindingURL.class);
+
+ String _url;
+ AMQShortString _exchangeClass = ExchangeDefaults.DIRECT_EXCHANGE_CLASS;
+ AMQShortString _exchangeName = new AMQShortString("");
+ AMQShortString _destinationName = new AMQShortString("");;
+ AMQShortString _queueName = new AMQShortString("");
+ AMQShortString[] _bindingKeys = new AMQShortString[0];
+ private HashMap<String, String> _options;
+
+ public AMQBindingURL(String url) throws URISyntaxException
+ {
+ // format:
+ // <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']*
+ _logger.debug("Parsing URL: " + url);
+ _url = url;
+ _options = new HashMap<String, String>();
+
+ parseBindingURL();
+ }
+
+ private void parseBindingURL() throws URISyntaxException
+ {
+ BindingURLParser parser = new BindingURLParser(_url,this);
+ processOptions();
+ _logger.debug("URL Parsed: " + this);
+ }
+
+ public void setExchangeClass(String exchangeClass)
+ {
+ setExchangeClass(new AMQShortString(exchangeClass));
+ }
+
+ public void setQueueName(String name)
+ {
+ setQueueName(new AMQShortString(name));
+ }
+
+ public void setDestinationName(String name)
+ {
+ setDestinationName(new AMQShortString(name));
+ }
+
+ public void setExchangeName(String exchangeName)
+ {
+ setExchangeName(new AMQShortString(exchangeName));
+ }
+
+ private void processOptions() throws URISyntaxException
+ {
+ }
+
+ public String getURL()
+ {
+ return _url;
+ }
+
+ public AMQShortString getExchangeClass()
+ {
+ return _exchangeClass;
+ }
+
+ private void setExchangeClass(AMQShortString exchangeClass)
+ {
+
+ _exchangeClass = exchangeClass;
+ if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ setOption(BindingURL.OPTION_EXCLUSIVE, "true");
+ }
+
+ }
+
+ public AMQShortString getExchangeName()
+ {
+ return _exchangeName;
+ }
+
+ private void setExchangeName(AMQShortString name)
+ {
+ _exchangeName = name;
+ }
+
+ public AMQShortString getDestinationName()
+ {
+ return _destinationName;
+ }
+
+ private void setDestinationName(AMQShortString name)
+ {
+ _destinationName = name;
+ }
+
+ public AMQShortString getQueueName()
+ {
+ return _queueName;
+ }
+
+ public void setQueueName(AMQShortString name)
+ {
+ _queueName = name;
+ }
+
+ public String getOption(String key)
+ {
+ return _options.get(key);
+ }
+
+ public void setOption(String key, String value)
+ {
+ _options.put(key, value);
+ }
+
+ public boolean containsOption(String key)
+ {
+ return _options.containsKey(key);
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ if (_exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
+ {
+ if (containsOption(BindingURL.OPTION_ROUTING_KEY))
+ {
+ return new AMQShortString((String)getOption(OPTION_ROUTING_KEY));
+ }
+ else
+ {
+ return getQueueName();
+ }
+ }
+
+ if (containsOption(BindingURL.OPTION_ROUTING_KEY))
+ {
+ return new AMQShortString((String)getOption(OPTION_ROUTING_KEY));
+ }
+
+ return getDestinationName();
+ }
+
+ public AMQShortString[] getBindingKeys()
+ {
+ if (_bindingKeys != null && _bindingKeys.length>0)
+ {
+ return _bindingKeys;
+ }
+ else
+ {
+ return new AMQShortString[]{getRoutingKey()};
+ }
+ }
+
+ public void setBindingKeys(AMQShortString[] keys)
+ {
+ _bindingKeys = keys;
+ }
+
+ public void setRoutingKey(AMQShortString key)
+ {
+ setOption(OPTION_ROUTING_KEY, key.toString());
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(_exchangeClass);
+ sb.append("://");
+ sb.append(_exchangeName);
+ sb.append('/');
+ sb.append(_destinationName);
+ sb.append('/');
+ sb.append(_queueName);
+
+ sb.append(URLHelper.printOptions(_options));
+
+ // temp hack
+ if (getRoutingKey() == null || getRoutingKey().toString().equals(""))
+ {
+
+ if (sb.toString().indexOf("?") == -1)
+ {
+ sb.append("?");
+ }
+ else
+ {
+ sb.append("&");
+ }
+
+ for (AMQShortString key :_bindingKeys)
+ {
+ sb.append(BindingURL.OPTION_BINDING_KEY).append("='").append(key.toString()).append("'&");
+ }
+
+ return sb.toString().substring(0,sb.toString().length()-1);
+ }
+ else
+ {
+ return sb.toString();
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java
new file mode 100644
index 0000000000..25450fea64
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.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.url;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/*
+ Binding URL format:
+ <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']*
+*/
+public interface BindingURL
+{
+ public static final String OPTION_EXCLUSIVE = "exclusive";
+ public static final String OPTION_AUTODELETE = "autodelete";
+ public static final String OPTION_DURABLE = "durable";
+ public static final String OPTION_CLIENTID = "clientid";
+ public static final String OPTION_SUBSCRIPTION = "subscription";
+ public static final String OPTION_ROUTING_KEY = "routingkey";
+ public static final String OPTION_BINDING_KEY = "bindingkey";
+
+
+ String getURL();
+
+ AMQShortString getExchangeClass();
+
+ AMQShortString getExchangeName();
+
+ AMQShortString getDestinationName();
+
+ AMQShortString getQueueName();
+
+ String getOption(String key);
+
+ boolean containsOption(String key);
+
+ AMQShortString getRoutingKey();
+
+ AMQShortString[] getBindingKeys();
+
+ String toString();
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java
new file mode 100644
index 0000000000..f73ae9c232
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java
@@ -0,0 +1,466 @@
+package org.apache.qpid.url;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BindingURLParser
+{
+ private static final char PROPERTY_EQUALS_CHAR = '=';
+ private static final char PROPERTY_SEPARATOR_CHAR = '&';
+ private static final char ALTERNATIVE_PROPERTY_SEPARATOR_CHAR = ',';
+ private static final char FORWARD_SLASH_CHAR = '/';
+ private static final char QUESTION_MARK_CHAR = '?';
+ private static final char SINGLE_QUOTE_CHAR = '\'';
+ private static final char COLON_CHAR = ':';
+ private static final char END_OF_URL_MARKER_CHAR = '%';
+
+ private static final Logger _logger = LoggerFactory.getLogger(BindingURLParser.class);
+
+ private char[] _url;
+ private AMQBindingURL _bindingURL;
+ private BindingURLParserState _currentParserState;
+ private String _error;
+ private int _index = 0;
+ private String _currentPropName;
+ private Map<String,Object> _options = new HashMap<String,Object>();
+
+ //<exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']*
+ public BindingURLParser(String url,AMQBindingURL bindingURL) throws URISyntaxException
+ {
+ _url = (url + END_OF_URL_MARKER_CHAR).toCharArray();
+ _bindingURL = bindingURL;
+ _currentParserState = BindingURLParserState.BINDING_URL_START;
+ BindingURLParserState prevState = _currentParserState;
+
+ try
+ {
+ while (_currentParserState != BindingURLParserState.ERROR && _currentParserState != BindingURLParserState.BINDING_URL_END)
+ {
+ prevState = _currentParserState;
+ _currentParserState = next();
+ }
+
+ if (_currentParserState == BindingURLParserState.ERROR)
+ {
+ _error =
+ "Invalid URL format [current_state = " + prevState + ", details parsed so far " + _bindingURL + " ] error at (" + _index + ") due to " + _error;
+ _logger.debug(_error);
+ URISyntaxException ex;
+ ex = new URISyntaxException(markErrorLocation(),"Error occured while parsing URL",_index);
+ throw ex;
+ }
+
+ processOptions();
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ _error = "Invalid URL format [current_state = " + prevState + ", details parsed so far " + _bindingURL + " ] error at (" + _index + ")";
+ URISyntaxException ex = new URISyntaxException(markErrorLocation(),"Error occured while parsing URL",_index);
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ enum BindingURLParserState
+ {
+ BINDING_URL_START,
+ EXCHANGE_CLASS,
+ COLON_CHAR,
+ DOUBLE_SEP,
+ EXCHANGE_NAME,
+ EXCHANGE_SEPERATOR_CHAR,
+ DESTINATION,
+ DESTINATION_SEPERATOR_CHAR,
+ QUEUE_NAME,
+ QUESTION_MARK_CHAR,
+ PROPERTY_NAME,
+ PROPERTY_EQUALS,
+ START_PROPERTY_VALUE,
+ PROPERTY_VALUE,
+ END_PROPERTY_VALUE,
+ PROPERTY_SEPARATOR,
+ BINDING_URL_END,
+ ERROR
+ }
+
+ /**
+ * I am fully ware that there are few optimizations
+ * that can speed up things a wee bit. But I have opted
+ * for readability and maintainability at the expense of
+ * speed, as speed is not a critical factor here.
+ *
+ * One can understand the full parse logic by just looking at this method.
+ */
+ private BindingURLParserState next()
+ {
+ switch (_currentParserState)
+ {
+ case BINDING_URL_START:
+ return extractExchangeClass();
+ case COLON_CHAR:
+ _index++; //skip ":"
+ return BindingURLParserState.DOUBLE_SEP;
+ case DOUBLE_SEP:
+ _index = _index + 2; //skip "//"
+ return BindingURLParserState.EXCHANGE_NAME;
+ case EXCHANGE_NAME:
+ return extractExchangeName();
+ case EXCHANGE_SEPERATOR_CHAR:
+ _index++; // skip '/'
+ return BindingURLParserState.DESTINATION;
+ case DESTINATION:
+ return extractDestination();
+ case DESTINATION_SEPERATOR_CHAR:
+ _index++; // skip '/'
+ return BindingURLParserState.QUEUE_NAME;
+ case QUEUE_NAME:
+ return extractQueueName();
+ case QUESTION_MARK_CHAR:
+ _index++; // skip '?'
+ return BindingURLParserState.PROPERTY_NAME;
+ case PROPERTY_NAME:
+ return extractPropertyName();
+ case PROPERTY_EQUALS:
+ _index++; // skip the equal sign
+ return BindingURLParserState.START_PROPERTY_VALUE;
+ case START_PROPERTY_VALUE:
+ _index++; // skip the '\''
+ return BindingURLParserState.PROPERTY_VALUE;
+ case PROPERTY_VALUE:
+ return extractPropertyValue();
+ case END_PROPERTY_VALUE:
+ _index ++;
+ return checkEndOfURL();
+ case PROPERTY_SEPARATOR:
+ _index++; // skip '&'
+ return BindingURLParserState.PROPERTY_NAME;
+ default:
+ return BindingURLParserState.ERROR;
+ }
+ }
+
+ private BindingURLParserState extractExchangeClass()
+ {
+ char nextChar = _url[_index];
+
+ // check for the following special cases.
+ // "myQueue?durable='true'" or just "myQueue";
+
+ StringBuilder builder = new StringBuilder();
+ while (nextChar != COLON_CHAR && nextChar != QUESTION_MARK_CHAR && nextChar != END_OF_URL_MARKER_CHAR)
+ {
+ builder.append(nextChar);
+ _index++;
+ nextChar = _url[_index];
+ }
+
+ // normal use case
+ if (nextChar == COLON_CHAR)
+ {
+ _bindingURL.setExchangeClass(builder.toString());
+ return BindingURLParserState.COLON_CHAR;
+ }
+ // "myQueue?durable='true'" use case
+ else if (nextChar == QUESTION_MARK_CHAR)
+ {
+ _bindingURL.setExchangeClass(ExchangeDefaults.DIRECT_EXCHANGE_CLASS.asString());
+ _bindingURL.setExchangeName("");
+ _bindingURL.setQueueName(builder.toString());
+ return BindingURLParserState.QUESTION_MARK_CHAR;
+ }
+ else
+ {
+ _bindingURL.setExchangeClass(ExchangeDefaults.DIRECT_EXCHANGE_CLASS.asString());
+ _bindingURL.setExchangeName("");
+ _bindingURL.setQueueName(builder.toString());
+ return BindingURLParserState.BINDING_URL_END;
+ }
+ }
+
+ private BindingURLParserState extractExchangeName()
+ {
+ char nextChar = _url[_index];
+ StringBuilder builder = new StringBuilder();
+ while (nextChar != FORWARD_SLASH_CHAR)
+ {
+ builder.append(nextChar);
+ _index++;
+ nextChar = _url[_index];
+ }
+
+ _bindingURL.setExchangeName(builder.toString());
+ return BindingURLParserState.EXCHANGE_SEPERATOR_CHAR;
+ }
+
+ private BindingURLParserState extractDestination()
+ {
+ char nextChar = _url[_index];
+
+ //The destination is and queue name are both optional
+ // This is checking for the case where both are not specified.
+ if (nextChar == QUESTION_MARK_CHAR)
+ {
+ return BindingURLParserState.QUESTION_MARK_CHAR;
+ }
+
+ StringBuilder builder = new StringBuilder();
+ while (nextChar != FORWARD_SLASH_CHAR && nextChar != QUESTION_MARK_CHAR)
+ {
+ builder.append(nextChar);
+ _index++;
+ nextChar = _url[_index];
+ }
+
+ // This is the case where the destination is explictily stated.
+ // ex direct://amq.direct/myDest/myQueue?option1='1' ... OR
+ // direct://amq.direct//myQueue?option1='1' ...
+ if (nextChar == FORWARD_SLASH_CHAR)
+ {
+ _bindingURL.setDestinationName(builder.toString());
+ return BindingURLParserState.DESTINATION_SEPERATOR_CHAR;
+ }
+ // This is the case where destination is not explictly stated.
+ // ex direct://amq.direct/myQueue?option1='1' ...
+ else
+ {
+ _bindingURL.setQueueName(builder.toString());
+ return BindingURLParserState.QUESTION_MARK_CHAR;
+ }
+ }
+
+ private BindingURLParserState extractQueueName()
+ {
+ char nextChar = _url[_index];
+ StringBuilder builder = new StringBuilder();
+ while (nextChar != QUESTION_MARK_CHAR && nextChar != END_OF_URL_MARKER_CHAR)
+ {
+ builder.append(nextChar);
+ nextChar = _url[++_index];
+ }
+ _bindingURL.setQueueName(builder.toString());
+
+ if(nextChar == QUESTION_MARK_CHAR)
+ {
+ return BindingURLParserState.QUESTION_MARK_CHAR;
+ }
+ else
+ {
+ return BindingURLParserState.BINDING_URL_END;
+ }
+ }
+
+ private BindingURLParserState extractPropertyName()
+ {
+ StringBuilder builder = new StringBuilder();
+ char next = _url[_index];
+ while (next != PROPERTY_EQUALS_CHAR)
+ {
+ builder.append(next);
+ next = _url[++_index];
+ }
+ _currentPropName = builder.toString();
+
+ if (_currentPropName.trim().equals(""))
+ {
+ _error = "Property name cannot be empty";
+ return BindingURLParserState.ERROR;
+ }
+
+ return BindingURLParserState.PROPERTY_EQUALS;
+ }
+
+ private BindingURLParserState extractPropertyValue()
+ {
+ StringBuilder builder = new StringBuilder();
+ char next = _url[_index];
+ while (next != SINGLE_QUOTE_CHAR)
+ {
+ builder.append(next);
+ next = _url[++_index];
+ }
+ String propValue = builder.toString();
+
+ if (propValue.trim().equals(""))
+ {
+ _error = "Property values cannot be empty";
+ return BindingURLParserState.ERROR;
+ }
+ else
+ {
+ if (_options.containsKey(_currentPropName))
+ {
+ Object obj = _options.get(_currentPropName);
+ if (obj instanceof List)
+ {
+ List list = (List)obj;
+ list.add(propValue);
+ }
+ else // it has to be a string
+ {
+ List<String> list = new ArrayList();
+ list.add((String)obj);
+ list.add(propValue);
+ _options.put(_currentPropName, list);
+ }
+ }
+ else
+ {
+ _options.put(_currentPropName, propValue);
+ }
+
+
+ return BindingURLParserState.END_PROPERTY_VALUE;
+ }
+ }
+
+ private BindingURLParserState checkEndOfURL()
+ {
+ char nextChar = _url[_index];
+ if ( nextChar == END_OF_URL_MARKER_CHAR)
+ {
+ return BindingURLParserState.BINDING_URL_END;
+ }
+ else if (nextChar == PROPERTY_SEPARATOR_CHAR || nextChar == ALTERNATIVE_PROPERTY_SEPARATOR_CHAR)
+ {
+ return BindingURLParserState.PROPERTY_SEPARATOR;
+ }
+ else
+ {
+ return BindingURLParserState.ERROR;
+ }
+ }
+
+ private String markErrorLocation()
+ {
+ String tmp = String.valueOf(_url);
+ // length -1 to remove ENDOF URL marker
+ return tmp.substring(0,_index) + "^" + tmp.substring(_index+1> tmp.length()-1?tmp.length()-1:_index+1,tmp.length()-1);
+ }
+
+ private void processOptions() throws URISyntaxException
+ {
+// check for bindingKey
+ if (_options.containsKey(BindingURL.OPTION_BINDING_KEY) && _options.get(BindingURL.OPTION_BINDING_KEY) != null)
+ {
+ Object obj = _options.get(BindingURL.OPTION_BINDING_KEY);
+
+ if (obj instanceof String)
+ {
+ AMQShortString[] bindingKeys = new AMQShortString[]{new AMQShortString((String)obj)};
+ _bindingURL.setBindingKeys(bindingKeys);
+ }
+ else // it would be a list
+ {
+ List list = (List)obj;
+ AMQShortString[] bindingKeys = new AMQShortString[list.size()];
+ int i=0;
+ for (Iterator it = list.iterator(); it.hasNext();)
+ {
+ bindingKeys[i] = new AMQShortString((String)it.next());
+ i++;
+ }
+ _bindingURL.setBindingKeys(bindingKeys);
+ }
+
+ }
+ for (String key: _options.keySet())
+ {
+ // We want to skip the bindingKey list
+ if (_options.get(key) instanceof String)
+ {
+ _bindingURL.setOption(key, (String)_options.get(key));
+ }
+ }
+
+
+ // check if both a binding key and a routing key is specified.
+ if (_options.containsKey(BindingURL.OPTION_BINDING_KEY) && _options.containsKey(BindingURL.OPTION_ROUTING_KEY))
+ {
+ throw new URISyntaxException(String.valueOf(_url),"It is illegal to specify both a routingKey and a bindingKey in the same URL",-1);
+ }
+
+ // check for durable subscriptions
+ if (_bindingURL.getExchangeClass().equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ String queueName = null;
+ if (Boolean.parseBoolean(_bindingURL.getOption(BindingURL.OPTION_DURABLE)))
+ {
+ if (_bindingURL.containsOption(BindingURL.OPTION_CLIENTID) && _bindingURL.containsOption(BindingURL.OPTION_SUBSCRIPTION))
+ {
+ queueName = _bindingURL.getOption(BindingURL.OPTION_CLIENTID + ":" + BindingURL.OPTION_SUBSCRIPTION);
+ }
+ else
+ {
+ throw new URISyntaxException(String.valueOf(_url),"Durable subscription must have values for " + BindingURL.OPTION_CLIENTID
+ + " and " + BindingURL.OPTION_SUBSCRIPTION , -1);
+
+ }
+ }
+ _bindingURL.setQueueName(queueName);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ String[] urls = new String[]
+ {
+ "topic://amq.topic//myTopic?routingkey='stocks.#'",
+ "topic://amq.topic/message_queue?bindingkey='usa.*'&bindingkey='control',exclusive='true'",
+ "topic://amq.topic//?bindingKey='usa.*',bindingkey='control',exclusive='true'",
+ "direct://amq.direct/dummyDest/myQueue?routingkey='abc.*'",
+ "exchange.Class://exchangeName/Destination/Queue",
+ "exchangeClass://exchangeName/Destination/?option='value',option2='value2'",
+ "IBMPerfQueue1?durable='true'",
+ "exchangeClass://exchangeName/Destination/?bindingkey='key1',bindingkey='key2'",
+ "exchangeClass://exchangeName/Destination/?bindingkey='key1'&routingkey='key2'"
+ };
+
+ try
+ {
+ for (String url: urls)
+ {
+ System.out.println("URL " + url);
+ AMQBindingURL bindingURL = new AMQBindingURL(url);
+ BindingURLParser parser = new BindingURLParser(url,bindingURL);
+ System.out.println("\nX " + bindingURL.toString() + " \n");
+
+ }
+
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLHelper.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLHelper.java
new file mode 100644
index 0000000000..2d496d2d54
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLHelper.java
@@ -0,0 +1,173 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.url;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class URLHelper
+{
+ public static char DEFAULT_OPTION_SEPERATOR = '&';
+ public static char ALTERNATIVE_OPTION_SEPARATOR = ',';
+ public static char BROKER_SEPARATOR = ';';
+
+ public static void parseOptions(Map<String, String> optionMap, String options) throws URLSyntaxException
+ {
+ // options looks like this
+ // brokerlist='tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value'',failover='method?option='value',option='value''
+
+ if ((options == null) || (options.indexOf('=') == -1))
+ {
+ return;
+ }
+
+ int optionIndex = options.indexOf('=');
+
+ String option = options.substring(0, optionIndex);
+
+ int length = options.length();
+
+ int nestedQuotes = 0;
+
+ // to store index of final "'"
+ int valueIndex = optionIndex;
+
+ // Walk remainder of url.
+ while ((nestedQuotes > 0) || (valueIndex < length))
+ {
+ valueIndex++;
+
+ if (valueIndex >= length)
+ {
+ break;
+ }
+
+ if (options.charAt(valueIndex) == '\'')
+ {
+ if ((valueIndex + 1) < options.length())
+ {
+ if ((options.charAt(valueIndex + 1) == DEFAULT_OPTION_SEPERATOR)
+ || (options.charAt(valueIndex + 1) == ALTERNATIVE_OPTION_SEPARATOR)
+ || (options.charAt(valueIndex + 1) == BROKER_SEPARATOR)
+ || (options.charAt(valueIndex + 1) == '\''))
+ {
+ nestedQuotes--;
+
+ if (nestedQuotes == 0)
+ {
+ // We've found the value of an option
+ break;
+ }
+ }
+ else
+ {
+ nestedQuotes++;
+ }
+ }
+ else
+ {
+ // We are at the end of the string
+ // Check to see if we are corectly closing quotes
+ if (options.charAt(valueIndex) == '\'')
+ {
+ nestedQuotes--;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ((nestedQuotes != 0) || (valueIndex < (optionIndex + 2)))
+ {
+ int sepIndex = 0;
+
+ // Try and identify illegal separator character
+ if (nestedQuotes > 1)
+ {
+ for (int i = 0; i < nestedQuotes; i++)
+ {
+ sepIndex = options.indexOf('\'', sepIndex);
+ sepIndex++;
+ }
+ }
+
+ if ((sepIndex >= options.length()) || (sepIndex == 0))
+ {
+ throw parseError(valueIndex, "Unterminated option", options);
+ }
+ else
+ {
+ throw parseError(sepIndex, "Unterminated option. Possible illegal option separator:'"
+ + options.charAt(sepIndex) + "'", options);
+ }
+ }
+
+ // optionIndex +2 to skip "='"
+ String value = options.substring(optionIndex + 2, valueIndex);
+
+ optionMap.put(option, value);
+
+ if (valueIndex < (options.length() - 1))
+ {
+ // Recurse to get remaining options
+ parseOptions(optionMap, options.substring(valueIndex + 2));
+ }
+ }
+
+ public static URLSyntaxException parseError(int index, String error, String url)
+ {
+ return parseError(index, 1, error, url);
+ }
+
+ public static URLSyntaxException parseError(int index, int length, String error, String url)
+ {
+ return new URLSyntaxException(url, error, index, length);
+ }
+
+ public static String printOptions(Map<String, String> options)
+ {
+ if (options.isEmpty())
+ {
+ return "";
+ }
+ else
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append('?');
+ for (String key : options.keySet())
+ {
+ sb.append(key);
+
+ sb.append("='");
+
+ sb.append(options.get(key));
+
+ sb.append("'");
+ sb.append(DEFAULT_OPTION_SEPERATOR);
+ }
+
+ sb.deleteCharAt(sb.length() - 1);
+
+ return sb.toString();
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java
new file mode 100644
index 0000000000..3ff7195794
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.url;
+
+import java.net.URISyntaxException;
+
+public class URLSyntaxException extends URISyntaxException
+{
+ private int _length;
+
+ public URLSyntaxException(String url, String error, int index, int length)
+ {
+ super(url, error, index);
+
+ _length = length;
+ }
+
+ private static String getPositionString(int index, int length)
+ {
+ StringBuffer sb = new StringBuffer(index + 1);
+
+ for (int i = 0; i < index; i++)
+ {
+ sb.append(" ");
+ }
+
+ if (length > -1)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ sb.append('^');
+ }
+ }
+
+ return sb.toString();
+ }
+
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(getReason());
+
+ if (getIndex() > -1)
+ {
+ if (_length != -1)
+ {
+ sb.append(" between indicies ");
+ sb.append(getIndex());
+ sb.append(" and ");
+ sb.append(_length);
+ }
+ else
+ {
+ sb.append(" at index ");
+ sb.append(getIndex());
+ }
+ }
+
+ sb.append(" ");
+ if (getIndex() != -1)
+ {
+ sb.append("\n");
+ }
+
+ sb.append(getInput());
+
+ if (getIndex() != -1)
+ {
+ sb.append("\n");
+ sb.append(getPositionString(getIndex(), _length));
+ }
+
+ return sb.toString();
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java
new file mode 100644
index 0000000000..baa68f87f9
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java
@@ -0,0 +1,689 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.*;
+
+/**
+ * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
+ * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
+ * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
+ * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
+ * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
+ * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
+ *
+ * <p/>Some example command lines are:
+ *
+ * <ul>
+ * <li>This one has two options that expect arguments:
+ * <pre>
+ * cruisecontrol -configfile cruisecontrol.xml -port 9000
+ * </pre>
+ * <li>This has one no-arg flag and two 'free' arguments:
+ * <pre>
+ * zip -r project.zip project/*
+ * </pre>
+ * <li>This one concatenates multiple flags into a single block with only one '-':
+ * <pre>
+ * jar -tvf mytar.tar
+ * </pre>
+ *
+ * <p/>The parsing rules are:
+ *
+ * <ol>
+ * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
+ * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
+ * <li>Options expecting arguments must always be on their own.
+ * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option.
+ * <li>The argument to an option may never begin with a '-' character.
+ * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
+ * <li>The second or later of a set of duplicate or repeated flags are ignored.
+ * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space
+ * between an option and its argument. This rules out the possibility of using two options where one is an opening
+ * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
+ * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
+ * the "bar" argument.
+ * </ol>
+ *
+ * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
+ * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept a command line specification.
+ * <tr><td> Parse a command line into properties, validating it against its specification.
+ * <tr><td> Report all errors between a command line and its specification.
+ * <tr><td> Provide a formatted usage string for a command line.
+ * <tr><td> Provide a formatted options in force string for a command line.
+ * <tr><td> Allow errors on unknowns behaviour to be turned on or off.
+ * </table>
+ */
+public class CommandLineParser
+{
+ /** Holds a mapping from command line option names to detailed information about those options. */
+ private Map<String, CommandLineOption> optionMap = new HashMap<String, CommandLineOption>();
+
+ /** Holds a list of parsing errors. */
+ private List<String> parsingErrors = new ArrayList<String>();
+
+ /** Holds the regular expression matcher to match command line options with. */
+ private Matcher optionMatcher = null;
+
+ /** Holds the parsed command line properties after parsing. */
+ private Properties parsedProperties = null;
+
+ /** Flag used to indicate that errors should be created for unknown options. False by default. */
+ private boolean errorsOnUnknowns = false;
+
+ /**
+ * Creates a command line options parser from a command line specification. This is passed to this constructor
+ * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
+ * array may therefore easily be used to configure the command line parser in a single method call with an easily
+ * readable format.
+ *
+ * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
+ * are assumed to be null. The elements specify the following parameters:
+ * <ol>
+ * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
+ * arguments use the option names "1", "2", ... and so on.
+ * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
+ * <li>The options argument. This is a very short description of the argument to the option, often a single word
+ * or a reminder as to the arguments format. When this element is null the option is a flag and does not
+ * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
+ * is only used to print in the usage message to remind the user of the usage of the option.
+ * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
+ * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
+ * this is ignored for flags.
+ * <li>A regular expression describing the format that the argument must take. Ignored if null.
+ * </ol>
+ * <p/>An example call to this constructor is:
+ *
+ * <pre>
+ * CommandLineParser commandLine = new CommandLineParser(
+ * new String[][] {{"file", "The file to be processed. ", "filename", "true"},
+ * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
+ * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
+ * {"v", "Verbose mode. Prints information about the processing as it goes."},
+ * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
+ * </pre>
+ *
+ * @param config The configuration as an array of arrays of strings.
+ */
+ public CommandLineParser(String[][] config)
+ {
+ // Loop through all the command line option specifications creating details for each in the options map.
+ for (int i = 0; i < config.length; i++)
+ {
+ String[] nextOptionSpec = config[i];
+
+ addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
+ (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false,
+ (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
+ }
+ }
+
+ /**
+ * Lists all the parsing errors from the most recent parsing in a string.
+ *
+ * @return All the parsing errors from the most recent parsing.
+ */
+ public String getErrors()
+ {
+ // Return the empty string if there are no errors.
+ if (parsingErrors.isEmpty())
+ {
+ return "";
+ }
+
+ // Concatenate all the parsing errors together.
+ String result = "";
+
+ for (String s : parsingErrors)
+ {
+ result += s;
+ }
+
+ return result;
+ }
+
+ /**
+ * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ *
+ * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ */
+ public String getOptionsInForce()
+ {
+ // Check if there are no properties to report and return and empty string if so.
+ if (parsedProperties == null)
+ {
+ return "";
+ }
+
+ // List all the properties.
+ String result = "Options in force:\n";
+
+ for (Map.Entry<Object, Object> property : parsedProperties.entrySet())
+ {
+ result += property.getKey() + " = " + property.getValue() + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Generates a usage string consisting of the name of each option and each options argument description and
+ * comment.
+ *
+ * @return A usage string for all the options.
+ */
+ public String getUsage()
+ {
+ String result = "Options:\n";
+
+ // Print usage on each of the command line options.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ result +=
+ optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "")
+ + optionInfo.comment + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
+ * as errors. When turned off, all unknowns are simply ignored.
+ *
+ * @param errors The setting of the errors on unkown flag. True to turn it on.
+ */
+ public void setErrorsOnUnknowns(boolean errors)
+ {
+ errorsOnUnknowns = errors;
+ }
+
+ /**
+ * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
+ * are keyed by integers as strings starting at "1" and then "2", ... and so on.
+ *
+ * <p/>See the class level comment for a description of the parsing rules.
+ *
+ * @param args The command line arguments.
+ *
+ * @return The arguments as a set of properties.
+ *
+ * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception
+ * is thrown a call to {@link #getErrors} will provide a diagnostic of the command
+ * line errors.
+ */
+ public Properties parseCommandLine(String[] args) throws IllegalArgumentException
+ {
+ Properties options = new Properties();
+
+ // Used to keep count of the current 'free' argument.
+ int free = 1;
+
+ // Used to indicate that the most recently parsed option is expecting arguments.
+ boolean expectingArgs = false;
+
+ // The option that is expecting arguments from the next element of the command line.
+ String optionExpectingArgs = null;
+
+ // Used to indicate that the most recently parsed option is a duplicate and should be ignored.
+ boolean ignore = false;
+
+ // Create the regular expression matcher for the command line options.
+ String regexp = "^(";
+ int optionsAdded = 0;
+
+ for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();)
+ {
+ String nextOption = i.next();
+
+ // Check that the option is not a free argument definition.
+ boolean notFree = false;
+
+ try
+ {
+ Integer.parseInt(nextOption);
+ }
+ catch (NumberFormatException e)
+ {
+ notFree = true;
+ }
+
+ // Add the option to the regular expression matcher if it is not a free argument definition.
+ if (notFree)
+ {
+ regexp += nextOption + (i.hasNext() ? "|" : "");
+ optionsAdded++;
+ }
+ }
+
+ // There has to be more that one option in the regular expression or else the compiler complains that the close
+ // cannot be nullable if the '?' token is used to make the matched option string optional.
+ regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
+ Pattern pattern = Pattern.compile(regexp);
+
+ // Loop through all the command line arguments.
+ for (int i = 0; i < args.length; i++)
+ {
+ // Check if the next command line argument begins with a '-' character and is therefore the start of
+ // an option.
+ if (args[i].startsWith("-"))
+ {
+ // Extract the value of the option without the leading '-'.
+ String arg = args[i].substring(1);
+
+ // Match up to the longest matching option.
+ optionMatcher = pattern.matcher(arg);
+ optionMatcher.matches();
+
+ String matchedOption = optionMatcher.group(1);
+
+ // Match any argument directly appended onto the longest matching option.
+ String matchedArg = optionMatcher.group(2);
+
+ // Check that a known option was matched.
+ if ((matchedOption != null) && !"".equals(matchedOption))
+ {
+ // Get the command line option information for the matched option.
+ CommandLineOption optionInfo = optionMap.get(matchedOption);
+
+ // Check if this option is expecting arguments.
+ if (optionInfo.expectsArgs)
+ {
+ // The option is expecting arguments so swallow the next command line argument as an
+ // argument to this option.
+ expectingArgs = true;
+ optionExpectingArgs = matchedOption;
+
+ // In the mean time set this options argument to the empty string in case no argument is ever
+ // supplied.
+ // options.put(matchedOption, "");
+ }
+
+ // Check if the option was matched on its own and is a flag in which case set that flag.
+ if ("".equals(matchedArg) && !optionInfo.expectsArgs)
+ {
+ options.put(matchedOption, "true");
+ }
+ // The option was matched as a substring with its argument appended to it or is a flag that is
+ // condensed together with other flags.
+ else if (!"".equals(matchedArg))
+ {
+ // Check if the option is a flag and therefore is allowed to be condensed together
+ // with other flags.
+ if (!optionInfo.expectsArgs)
+ {
+ // Set the first matched flag.
+ options.put(matchedOption, "true");
+
+ // Repeat the longest matching process on the remainder but ensure that the remainder
+ // consists only of flags as only flags may be condensed together in this fashion.
+ do
+ {
+ // Match the remainder against the options.
+ optionMatcher = pattern.matcher(matchedArg);
+ optionMatcher.matches();
+
+ matchedOption = optionMatcher.group(1);
+ matchedArg = optionMatcher.group(2);
+
+ // Check that an option was matched.
+ if (matchedOption != null)
+ {
+ // Get the command line option information for the next matched option.
+ optionInfo = optionMap.get(matchedOption);
+
+ // Ensure that the next option is a flag or raise an error if not.
+ if (optionInfo.expectsArgs == true)
+ {
+ parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
+ }
+
+ options.put(matchedOption, "true");
+ }
+ // The remainder could not be matched against a flag it is either an unknown flag
+ // or an illegal argument to a flag.
+ else
+ {
+ parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
+
+ break;
+ }
+ }
+ // Continue until the remainder of the argument has all been matched with flags.
+ while (!"".equals(matchedArg));
+ }
+ // The option is expecting an argument, so store the unmatched portion against it
+ // as its argument.
+ else
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, matchedArg);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(matchedOption, matchedArg);
+
+ // The argument to this flag has already been supplied to it. Do not swallow the
+ // next command line argument as an argument to this flag.
+ expectingArgs = false;
+ }
+ }
+ }
+ else // No matching option was found.
+ {
+ // Add this to the list of parsing errors if errors on unkowns is being used.
+ if (errorsOnUnknowns)
+ {
+ parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
+ }
+ }
+ }
+ // The command line argument did not being with a '-' so it is an argument to the previous flag or it
+ // is a free argument.
+ else
+ {
+ // Check if a previous flag is expecting to swallow this next argument as its argument.
+ if (expectingArgs)
+ {
+ // Get the option info for the option waiting for arguments.
+ CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
+
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(optionExpectingArgs, args[i]);
+
+ // Clear the expecting args flag now that the argument has been swallowed.
+ expectingArgs = false;
+ optionExpectingArgs = null;
+ }
+ // This command line option is not an argument to any option. Add it to the set of 'free' options.
+ else
+ {
+ // Get the option info for the free option, if there is any.
+ CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
+
+ if (optionInfo != null)
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+ }
+
+ // Add to the list of free options.
+ options.put(Integer.toString(free), args[i]);
+
+ // Move on to the next free argument.
+ free++;
+ }
+ }
+ }
+
+ // Scan through all the specified options to check that all mandatory options have been set and that all flags
+ // that were not set are set to false in the set of properties.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ // Check if this is a flag.
+ if (!optionInfo.expectsArgs)
+ {
+ // Check if the flag is not set in the properties and set it to false if so.
+ if (!options.containsKey(optionInfo.option))
+ {
+ options.put(optionInfo.option, "false");
+ }
+ }
+ // Check if this is a mandatory option and was not set.
+ else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
+ {
+ // Create an error for the missing option.
+ parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
+ }
+ }
+
+ // Check if there were any errors.
+ if (!parsingErrors.isEmpty())
+ {
+ // Throw an illegal argument exception to signify that there were parsing errors.
+ throw new IllegalArgumentException();
+ }
+
+ // Convert any name/value pairs in the free arguments into properties in the parsed options.
+ options = takeFreeArgsAsProperties(options, 1);
+
+ parsedProperties = options;
+
+ return options;
+ }
+
+ /**
+ * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties.
+ */
+ public void addCommandLineToProperties(Properties properties)
+ {
+ if (parsedProperties != null)
+ {
+ for (Object propKey : parsedProperties.keySet())
+ {
+ String name = (String) propKey;
+ String value = parsedProperties.getProperty(name);
+
+ properties.setProperty(name, value);
+ }
+ }
+ }
+
+ /**
+ * Resets this command line parser after it has been used to parse a command line. This method will only need
+ * to be called to use this parser a second time which is not likely seeing as a command line is usually only
+ * specified once. However, it is exposed as a public method for the rare case where this may be done.
+ *
+ * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in
+ * force.
+ */
+ public void reset()
+ {
+ parsingErrors = new ArrayList<String>();
+ parsedProperties = null;
+ }
+
+ /**
+ * Adds the option to list of available command line options.
+ *
+ * @param option The option to add as an available command line option.
+ * @param comment A comment for the option.
+ * @param argument The text that appears after the option in the usage string.
+ * @param mandatory When true, indicates that this option is mandatory.
+ * @param formatRegexp The format that the argument must take, defined as a regular expression.
+ */
+ protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
+ {
+ // Check if usage text has been set in which case this option is expecting arguments.
+ boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true;
+
+ // Add the option to the map of command line options.
+ CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
+ optionMap.put(option, opt);
+ }
+
+ /**
+ * Converts the free arguments into property declarations. After parsing the command line the free arguments
+ * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
+ * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
+ * 'value'.
+ *
+ * <p/>For example the comand line:
+ * <pre>
+ * ... debug=true
+ * </pre>
+ *
+ * <p/>After parsing has properties:
+ * <pre>[[1, debug=true]]</pre>
+ *
+ * <p/>After applying this method the properties are:
+ * <pre>[[1, debug=true], [debug, true]]</pre>
+ *
+ * @param properties The parsed command line properties.
+ * @param from The free argument index to convert to properties from.
+ *
+ * @return The parsed command line properties, with free argument name value pairs too.
+ */
+ private Properties takeFreeArgsAsProperties(Properties properties, int from)
+ {
+ for (int i = from; true; i++)
+ {
+ String nextFreeArg = properties.getProperty(Integer.toString(i));
+
+ // Terminate the loop once all free arguments have been consumed.
+ if (nextFreeArg == null)
+ {
+ break;
+ }
+
+ // Split it on the =, strip any whitespace and set it as a system property.
+ String[] nameValuePair = nextFreeArg.split("=");
+
+ if (nameValuePair.length == 2)
+ {
+ properties.setProperty(nameValuePair[0], nameValuePair[1]);
+ }
+ }
+
+ return properties;
+ }
+
+ /**
+ * Checks the format of an argument to an option against its specified regular expression format if one has
+ * been set. Any errors are added to the list of parsing errors.
+ *
+ * @param optionInfo The command line option information for the option which is havings its argument checked.
+ * @param matchedArg The string argument to the option.
+ */
+ private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
+ {
+ // Check if this option enforces a format for its argument.
+ if (optionInfo.argumentFormatRegexp != null)
+ {
+ Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
+ Matcher argumentMatcher = pattern.matcher(matchedArg);
+
+ // Check if the argument does not meet its required format.
+ if (!argumentMatcher.matches())
+ {
+ // Create an error for this badly formed argument.
+ parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
+ }
+ }
+ }
+
+ /**
+ * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
+ * a map of properties containing them.
+ *
+ * @param args The command line.
+ * @param commandLine The command line parser.
+ * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>).
+ *
+ * @return A set of properties containing all name=value pairs from the command line.
+ */
+ public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
+ {
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ Properties options = null;
+
+ try
+ {
+ options = commandLine.parseCommandLine(args);
+
+ // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up
+ // from there.
+ commandLine.addCommandLineToProperties(properties);
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ return options;
+ }
+
+ /**
+ * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
+ * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
+ * regular expression format is.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Hold details of a command line option.
+ * </table>
+ */
+ protected class CommandLineOption
+ {
+ /** Holds the text for the flag to match this argument with. */
+ public String option = null;
+
+ /** Holds a string describing how to use this command line argument. */
+ public String argument = null;
+
+ /** Flag that determines whether or not this command line argument can take arguments. */
+ public boolean expectsArgs = false;
+
+ /** Holds a short comment describing what this command line argument is for. */
+ public String comment = null;
+
+ /** Flag that determines whether or not this is an mandatory command line argument. */
+ public boolean mandatory = false;
+
+ /** A regular expression describing what format the argument to this option muist have. */
+ public String argumentFormatRegexp = null;
+
+ /**
+ * Create a command line option object that holds specific information about a command line option.
+ *
+ * @param option The text that matches the option.
+ * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
+ * @param comment A comment explaining how to use this option.
+ * @param argument A short reminder of the format of the argument to this option/
+ * @param mandatory Set to true if this option is mandatory.
+ * @param formatRegexp The regular expression that the argument to this option must meet to be valid.
+ */
+ public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
+ String formatRegexp)
+ {
+ this.option = option;
+ this.expectsArgs = expectsArgs;
+ this.comment = comment;
+ this.argument = argument;
+ this.mandatory = mandatory;
+ this.argumentFormatRegexp = formatRegexp;
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
new file mode 100644
index 0000000000..633cf4fe3a
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ConcurrentLinkedMessageQueueAtomicSize<E> extends ConcurrentLinkedQueueAtomicSize<E> implements MessageQueue<E>
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ConcurrentLinkedMessageQueueAtomicSize.class);
+
+ protected Queue<E> _messageHead = new ConcurrentLinkedQueueAtomicSize<E>();
+
+ protected AtomicInteger _messageHeadSize = new AtomicInteger(0);
+
+ @Override
+ public int size()
+ {
+ return super.size() + _messageHeadSize.get();
+ }
+
+ public int headSize()
+ {
+ return _messageHeadSize.get();
+ }
+
+ @Override
+ public E poll()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.poll();
+ }
+ else
+ {
+ E e = _messageHead.poll();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Providing item(" + e + ")from message head");
+ }
+
+ if (e != null)
+ {
+ _messageHeadSize.decrementAndGet();
+ }
+
+ return e;
+ }
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+
+ if (_messageHead.isEmpty())
+ {
+ return super.remove(o);
+ }
+ else
+ {
+ if (_messageHead.remove(o))
+ {
+ _messageHeadSize.decrementAndGet();
+
+ return true;
+ }
+
+ return super.remove(o);
+ }
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c)
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.removeAll(c);
+ }
+ else
+ {
+ // fixme this is super.removeAll but iterator here doesn't work
+ // we need to be able to correctly decrement _messageHeadSize
+ // boolean modified = false;
+ // Iterator<?> e = iterator();
+ // while (e.hasNext())
+ // {
+ // if (c.contains(e.next()))
+ // {
+ // e.remove();
+ // modified = true;
+ // _size.decrementAndGet();
+ // }
+ // }
+ // return modified;
+
+ throw new RuntimeException("Not implemented");
+ }
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return (_messageHead.isEmpty() && super.isEmpty());
+ }
+
+ @Override
+ public void clear()
+ {
+ super.clear();
+ _messageHead.clear();
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ return _messageHead.contains(o) || super.contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> o)
+ {
+ return _messageHead.containsAll(o) || super.containsAll(o);
+ }
+
+ @Override
+ public E element()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.element();
+ }
+ else
+ {
+ return _messageHead.element();
+ }
+ }
+
+ @Override
+ public E peek()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.peek();
+ }
+ else
+ {
+ E o = _messageHead.peek();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Peeking item (" + o + ") from message head");
+ }
+
+ return o;
+ }
+
+ }
+
+ @Override
+ public Iterator<E> iterator()
+ {
+ final Iterator<E> mainMessageIterator = super.iterator();
+
+ return new Iterator<E>()
+ {
+ final Iterator<E> _headIterator = _messageHead.iterator();
+ final Iterator<E> _mainIterator = mainMessageIterator;
+
+ Iterator<E> last;
+
+ public boolean hasNext()
+ {
+ return _headIterator.hasNext() || _mainIterator.hasNext();
+ }
+
+ public E next()
+ {
+ if (_headIterator.hasNext())
+ {
+ last = _headIterator;
+
+ return _headIterator.next();
+ }
+ else
+ {
+ last = _mainIterator;
+
+ return _mainIterator.next();
+ }
+ }
+
+ public void remove()
+ {
+ last.remove();
+ if(last == _mainIterator)
+ {
+ _size.decrementAndGet();
+ }
+ else
+ {
+ _messageHeadSize.decrementAndGet();
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c)
+ {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Override
+ public Object[] toArray()
+ {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ public boolean pushHead(E o)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Adding item(" + o + ") to head of queue");
+ }
+
+ if (_messageHead.offer(o))
+ {
+ _messageHeadSize.incrementAndGet();
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java
new file mode 100644
index 0000000000..c4d7683a02
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.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.util;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ConcurrentLinkedQueueAtomicSize<E> extends ConcurrentLinkedQueue<E>
+{
+ AtomicInteger _size = new AtomicInteger(0);
+
+ public int size()
+ {
+ return _size.get();
+ }
+
+ public boolean offer(E o)
+ {
+
+ if (super.offer(o))
+ {
+ _size.incrementAndGet();
+ return true;
+ }
+
+ return false;
+ }
+
+ public E poll()
+ {
+ E e = super.poll();
+
+ if (e != null)
+ {
+ _size.decrementAndGet();
+ }
+
+ return e;
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+ if (super.remove(o))
+ {
+ _size.decrementAndGet();
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java
new file mode 100644
index 0000000000..1f168345a1
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ConcurrentLinkedQueueNoSize<E> extends ConcurrentLinkedQueue<E>
+{
+ public int size()
+ {
+ if (isEmpty())
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
new file mode 100644
index 0000000000..3e13259ee3
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
@@ -0,0 +1,316 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.io.*;
+
+/**
+ * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all
+ * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to
+ * recover from errors.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read a text file as a string.
+ * <tr><td> Open a file or default resource as an input stream.
+ * </table>
+ */
+public class FileUtils
+{
+ /**
+ * Reads a text file as a string.
+ *
+ * @param filename The name of the file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(String filename)
+ {
+ BufferedInputStream is = null;
+
+ try{
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(filename));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }finally {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reads a text file as a string.
+ *
+ * @param file The file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(File file)
+ {
+ BufferedInputStream is = null;
+
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(file));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }
+
+ /**
+ * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all
+ * together as a string.
+ *
+ * @param is The reader.
+ *
+ * @return The contents of the reader.
+ */
+ private static String readStreamAsString(BufferedInputStream is)
+ {
+ try
+ {
+ byte[] data = new byte[4096];
+
+ StringBuffer inBuffer = new StringBuffer();
+
+ String line;
+ int read;
+
+ while ((read = is.read(data)) != -1)
+ {
+ String s = new String(data, 0, read);
+ inBuffer.append(s);
+ }
+
+ return inBuffer.toString();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Either opens the specified filename as an input stream, or uses the default resource loaded using the
+ * specified class loader, if opening the file fails or no file name is specified.
+ *
+ * @param filename The name of the file to open.
+ * @param defaultResource The name of the default resource on the classpath if the file cannot be opened.
+ * @param cl The classloader to load the default resource with.
+ *
+ * @return An input stream for the file or resource, or null if one could not be opened.
+ */
+ public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl)
+ {
+ InputStream is = null;
+
+ // Flag to indicate whether the default resource should be used. By default this is true, so that the default
+ // is used when opening the file fails.
+ boolean useDefault = true;
+
+ // Try to open the file if one was specified.
+ if (filename != null)
+ {
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(new File(filename)));
+
+ // Clear the default flag because the file was succesfully opened.
+ useDefault = false;
+ }
+ catch (FileNotFoundException e)
+ {
+ // Ignore this exception, the default will be used instead.
+ }
+ }
+
+ // Load the default resource if a file was not specified, or if opening the file failed.
+ if (useDefault)
+ {
+ is = cl.getResourceAsStream(defaultResource);
+ }
+
+ return is;
+ }
+
+ /**
+ * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist,
+ * it is created.
+ *
+ * @param src The source file name.
+ * @param dst The destination file name.
+ */
+ public static void copy(File src, File dst)
+ {
+ try
+ {
+ InputStream in = new FileInputStream(src);
+ if (!dst.exists())
+ {
+ dst.createNewFile();
+ }
+
+ OutputStream out = new FileOutputStream(dst);
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /*
+ * Deletes a given file
+ */
+ public static boolean deleteFile(String filePath)
+ {
+ return delete(new File(filePath), false);
+ }
+
+ /*
+ * Deletes a given empty directory
+ */
+ public static boolean deleteDirectory(String directoryPath)
+ {
+ File directory = new File(directoryPath);
+
+ if (directory.isDirectory())
+ {
+ if (directory.listFiles().length == 0)
+ {
+ return delete(directory, true);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete a given file/directory,
+ * A directory will always require the recursive flag to be set.
+ * if a directory is specified and recursive set then delete the whole tree
+ * @param file the File object to start at
+ * @param recursive boolean to recurse if a directory is specified.
+ * @return <code>true</code> if and only if the file or directory is
+ * successfully deleted; <code>false</code> otherwise
+ */
+ public static boolean delete(File file, boolean recursive)
+ {
+ boolean success = true;
+
+ if (file.isDirectory())
+ {
+ if (recursive)
+ {
+ for (File subFile : file.listFiles())
+ {
+ success = delete(subFile, true) & success ;
+ }
+
+ return file.delete();
+ }
+
+ return false;
+ }
+
+ return success && file.delete();
+ }
+
+
+ public static class UnableToCopyException extends Exception
+ {
+ UnableToCopyException(String msg)
+ {
+ super(msg);
+ }
+ }
+
+ public static void copyRecursive(File source, File dst) throws FileNotFoundException, UnableToCopyException
+ {
+
+ if (!source.exists())
+ {
+ throw new FileNotFoundException("Unable to copy '" + source.toString() + "' as it does not exist.");
+ }
+
+ if (dst.exists() && !dst.isDirectory())
+ {
+ throw new IllegalArgumentException("Unable to copy '" + source.toString() + "' to '" + dst + "' a file with same name exists.");
+ }
+
+
+ if (source.isFile())
+ {
+ copy(source, dst);
+ }
+
+ //else we have a source directory
+ if (!dst.isDirectory() && !dst.mkdir())
+ {
+ throw new UnableToCopyException("Unable to create destination directory");
+ }
+
+
+ for (File file : source.listFiles())
+ {
+ if (file.isFile())
+ {
+ copy(file, new File(dst.toString() + File.separator + file.getName()));
+ }
+ else
+ {
+ copyRecursive(file, new File(dst + File.separator + file.getName()));
+ }
+ }
+
+
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java
new file mode 100644
index 0000000000..b5efaa61b6
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.Queue;
+
+/**
+ * Defines a queue that has a push operation to add an element to the head of the queue.
+ *
+ * @todo Seems like this may be pointless, the implementation uses this method to increment the message count
+ * then calls offer. Why not simply override offer and drop this interface?
+ */
+public interface MessageQueue<E> extends Queue<E>
+{
+ /**
+ * Inserts the specified element into this queue, if possible. When using queues that may impose insertion
+ * restrictions (for example capacity bounds), method offer is generally preferable to method Collection.add(E),
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param o The element to insert.
+ *
+ * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt>
+ */
+ boolean pushHead(E o);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.java
new file mode 100644
index 0000000000..e764c8536b
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.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.util;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+
+/**
+ * NameUUIDGen
+ *
+ */
+
+public final class NameUUIDGen implements UUIDGen
+{
+
+ private static final int WIDTH = 8;
+
+ final private byte[] seed;
+ final private ByteBuffer seedBuf;
+ private long counter;
+
+ public NameUUIDGen()
+ {
+ String namespace = UUID.randomUUID().toString();
+ this.seed = new byte[namespace.length() + WIDTH];
+ for (int i = WIDTH; i < seed.length; i++)
+ {
+ seed[i] = (byte) namespace.charAt(i - WIDTH);
+ }
+ this.seedBuf = ByteBuffer.wrap(seed);
+ this.counter = 0;
+ }
+
+ public UUID generate()
+ {
+ seedBuf.putLong(0, counter++);
+ return UUID.nameUUIDFromBytes(seed);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java
new file mode 100644
index 0000000000..4677713dc9
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+/**
+ * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays.
+ */
+public class PrettyPrintingUtils
+{
+ /**
+ * Pretty prints an array of ints as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(int[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+
+ /**
+ * Pretty prints an array of strings as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(String[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java
new file mode 100644
index 0000000000..d90e3b1a17
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java
@@ -0,0 +1,200 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * PropertiesHelper defines some static methods which are useful when working with properties
+ * files.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read properties from an input stream
+ * <tr><td> Read properties from a file
+ * <tr><td> Read properties from a URL
+ * <tr><td> Read properties given a path to a file
+ * <tr><td> Trim any whitespace from property values
+ * </table>
+ */
+public class PropertiesUtils
+{
+ /** Used for logging. */
+ private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
+
+ /**
+ * Get properties from an input stream.
+ *
+ * @param is The input stream.
+ *
+ * @return The properties loaded from the input stream.
+ *
+ * @throws IOException If the is an I/O error reading from the stream.
+ */
+ public static Properties getProperties(InputStream is) throws IOException
+ {
+ log.debug("getProperties(InputStream): called");
+
+ // Create properties object laoded from input stream
+ Properties properties = new Properties();
+
+ properties.load(is);
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a file.
+ *
+ * @param file The file.
+ *
+ * @return The properties loaded from the file.
+ *
+ * @throws IOException If there is an I/O error reading from the file.
+ */
+ public static Properties getProperties(File file) throws IOException
+ {
+ log.debug("getProperties(File): called");
+
+ // Open the file as an input stream
+ InputStream is = new FileInputStream(file);
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the file
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a url.
+ *
+ * @param url The URL.
+ *
+ * @return The properties loaded from the url.
+ *
+ * @throws IOException If there is an I/O error reading from the URL.
+ */
+ public static Properties getProperties(URL url) throws IOException
+ {
+ log.debug("getProperties(URL): called");
+
+ // Open the URL as an input stream
+ InputStream is = url.openStream();
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the url
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a path name. The path name may refer to either a file or a URL.
+ *
+ * @param pathname The path name.
+ *
+ * @return The properties loaded from the file or URL.
+ *
+ * @throws IOException If there is an I/O error reading from the URL or file named by the path.
+ */
+ public static Properties getProperties(String pathname) throws IOException
+ {
+ log.debug("getProperties(String): called");
+
+ // Check that the path is not null
+ if (pathname == null)
+ {
+ return null;
+ }
+
+ // Check if the path is a URL
+ if (isURL(pathname))
+ {
+ // The path is a URL
+ return getProperties(new URL(pathname));
+ }
+ else
+ {
+ // Assume the path is a file name
+ return getProperties(new File(pathname));
+ }
+ }
+
+ /**
+ * Trims whitespace from property values. This method returns a new set of properties
+ * the same as the properties specified as an argument but with any white space removed by
+ * the {@link java.lang.String#trim} method.
+ *
+ * @param properties The properties to trim whitespace from.
+ *
+ * @return The white space trimmed properties.
+ */
+ public static Properties trim(Properties properties)
+ {
+ Properties trimmedProperties = new Properties();
+
+ // Loop over all the properties
+ for (Iterator i = properties.keySet().iterator(); i.hasNext();)
+ {
+ String next = (String) i.next();
+ String nextValue = properties.getProperty(next);
+
+ // Trim the value if it is not null
+ if (nextValue != null)
+ {
+ nextValue.trim();
+ }
+
+ // Store the trimmed value in the trimmed properties
+ trimmedProperties.setProperty(next, nextValue);
+ }
+
+ return trimmedProperties;
+ }
+
+ /**
+ * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with
+ * http:, ftp:, or uucp:.
+ *
+ * @param name The string to test for being a URL.
+ *
+ * @return True if the string is a URL and false if not.
+ */
+ private static boolean isURL(String name)
+ {
+ return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:")
+ || name.toLowerCase().startsWith("uucp:"));
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.java
new file mode 100644
index 0000000000..60b402a105
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.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.util;
+
+import java.util.UUID;
+
+
+/**
+ * RandomUUIDGen
+ *
+ */
+
+public final class RandomUUIDGen implements UUIDGen
+{
+
+ public UUID generate()
+ {
+ return UUID.randomUUID();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Serial.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Serial.java
new file mode 100644
index 0000000000..8ad9d00f54
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Serial.java
@@ -0,0 +1,108 @@
+package org.apache.qpid.util;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.util.Comparator;
+
+import org.apache.qpid.SerialException;
+
+/**
+ * This class provides basic serial number comparisons as defined in
+ * RFC 1982.
+ */
+
+public class Serial
+{
+
+ public static final Comparator<Integer> COMPARATOR = new Comparator<Integer>()
+ {
+ public int compare(Integer s1, Integer s2)
+ {
+ return Serial.compare(s1, s2);
+ }
+ };
+
+ /**
+ * Compares two numbers using serial arithmetic.
+ *
+ * @param s1 the first serial number
+ * @param s2 the second serial number
+ *
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is less than, equal to, or greater than the
+ * second
+ */
+ public static final int compare(int s1, int s2)
+ {
+ return s1 - s2;
+ }
+
+ public static final boolean lt(int s1, int s2)
+ {
+ return compare(s1, s2) < 0;
+ }
+
+ public static final boolean le(int s1, int s2)
+ {
+ return compare(s1, s2) <= 0;
+ }
+
+ public static final boolean gt(int s1, int s2)
+ {
+ return compare(s1, s2) > 0;
+ }
+
+ public static final boolean ge(int s1, int s2)
+ {
+ return compare(s1, s2) >= 0;
+ }
+
+ public static final boolean eq(int s1, int s2)
+ {
+ return s1 == s2;
+ }
+
+ public static final int min(int s1, int s2)
+ {
+ if (lt(s1, s2))
+ {
+ return s1;
+ }
+ else
+ {
+ return s2;
+ }
+ }
+
+ public static final int max(int s1, int s2)
+ {
+ if (gt(s1, s2))
+ {
+ return s1;
+ }
+ else
+ {
+ return s2;
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Strings.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Strings.java
new file mode 100644
index 0000000000..a0bbbb22de
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/Strings.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * Strings
+ *
+ */
+
+public final class Strings
+{
+
+ private static final byte[] EMPTY = new byte[0];
+
+ private static final ThreadLocal<char[]> charbuf = new ThreadLocal()
+ {
+ public char[] initialValue()
+ {
+ return new char[4096];
+ }
+ };
+
+ public static final byte[] toUTF8(String str)
+ {
+ if (str == null)
+ {
+ return EMPTY;
+ }
+ else
+ {
+ final int size = str.length();
+ char[] chars = charbuf.get();
+ if (size > chars.length)
+ {
+ chars = new char[Math.max(size, 2*chars.length)];
+ charbuf.set(chars);
+ }
+
+ str.getChars(0, size, chars, 0);
+ final byte[] bytes = new byte[size];
+ for (int i = 0; i < size; i++)
+ {
+ if (chars[i] > 127)
+ {
+ try
+ {
+ return str.getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ bytes[i] = (byte) chars[i];
+ }
+ return bytes;
+ }
+ }
+
+ public static final String fromUTF8(byte[] bytes)
+ {
+ try
+ {
+ return new String(bytes, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java
new file mode 100644
index 0000000000..3cfe5afdac
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+
+import java.util.UUID;
+
+/**
+ * UUIDGen
+ *
+ */
+
+public interface UUIDGen
+{
+
+ public UUID generate();
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDs.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDs.java
new file mode 100644
index 0000000000..4bf6b7f0a2
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDs.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.util;
+
+
+/**
+ * UUIDs
+ *
+ */
+
+public final class UUIDs
+{
+
+ public static final UUIDGen newGenerator()
+ {
+ return newGenerator(System.getProperty("qpid.uuid.generator",
+ NameUUIDGen.class.getName()));
+ }
+
+ public static UUIDGen newGenerator(String name)
+ {
+ try
+ {
+ Class cls = Class.forName(name);
+ return (UUIDGen) cls.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java
new file mode 100644
index 0000000000..e0c0337898
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java
@@ -0,0 +1,34 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a
+ * {@link BatchSynchQueue} because the producer has already been unblocked by an unblocking take on the queue.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Signal that an unblocking take has already occurred.
+ * </table>
+ */
+public class AlreadyUnblockedException extends RuntimeException
+{ }
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java
new file mode 100644
index 0000000000..63d8f77edb
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java
@@ -0,0 +1,122 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.Collection;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * BatchSynchQueue is an abstraction of the classic producer/consumer buffer pattern for thread interaction. In this
+ * pattern threads can deposit data onto a buffer whilst other threads take data from the buffer and perform usefull
+ * work with it. A BatchSynchQueue adds to this the possibility that producers can be blocked until their data is
+ * consumed or until a consumer chooses to release the producer some time after consuming the data from the queue.
+ *
+ * <p>There are a number of possible advantages to using this technique when compared with having the producers
+ * processing their own data:
+ *
+ * <ul>
+ * <li>Data may be deposited asynchronously in the buffer allowing the producers to continue running.</li>
+ * <li>Data may be deposited synchronously in the buffer so that producers wait until their data has been processed
+ * before being allowed to continue.</li>
+ * <li>Variable rates of production/consumption can be smoothed over by the buffer as it provides space in memory to
+ * hold data between production and consumption.</li>
+ * <li>Consumers may be able to batch data as they consume it leading to more efficient consumption over
+ * individual data item consumption where latency associated with the consume operation can be ammortized.
+ * For example, it may be possibly to ammortize the cost of a disk seek over many producers.</li>
+ * <li>Data from seperate threads can be combined together in the buffer, providing a convenient way of spreading work
+ * amongst many workers and gathering the results together again.</li>
+ * <li>Different types of queue can be used to hold the buffer, resulting in different processing orders. For example,
+ * lifo, fifo, priority heap, etc.</li>
+ * </ul>
+ *
+ * <p/>The asynchronous type of producer/consumer buffers is already well supported by the java.util.concurrent package
+ * (in Java 5) and there is also a synchronous queue implementation available there too. This interface extends the
+ * blocking queue with some more methods for controlling a synchronous blocking queue. In particular it adds additional
+ * take methods that can be used to take data from a queue without releasing producers, so that consumers have an
+ * opportunity to confirm correct processing of the data before producers are released. It also adds a put method with
+ * exceptions so that consumers can signal exception cases back to producers where there are errors in the data.
+ *
+ * <p/>This type of queue is usefull in situations where consumers can obtain an efficiency gain by batching data
+ * from many threads but where synchronous handling of that data is neccessary because producers need to know that
+ * their data has been processed before they continue. For example, sending a bundle of messages together, or writing
+ * many records to disk at once, may result in improved performance but the originators of the messages or disk records
+ * need confirmation that their data has really been sent or saved to disk.
+ *
+ * <p/>The consumer can put an element back onto the queue or send an error message to the elements producer using the
+ * {@link SynchRecord} interface.
+ *
+ * <p/>The {@link #take()}, {@link #drainTo(java.util.Collection<? super E>)} and
+ * {@link #drainTo(java.util.Collection<? super E>, int)} methods from {@link BlockingQueue} should behave as if they
+ * have been called with unblock set to false. That is they take elements from the queue but leave the producers
+ * blocked. These methods do not return collections of {@link SynchRecord}s so they do not supply an interface through
+ * which errors or re-queuings can be applied. If these methods are used then the consumer must succesfully process
+ * all the records it takes.
+ *
+ * <p/>The {@link #put} method should silently swallow any exceptions that consumers attempt to return to the caller.
+ * In order to handle exceptions the {@link #tryPut} method must be used.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Handle synchronous puts, with possible exceptions.
+ * <tr><td> Allow consumers to take many records from a queue in a batch.
+ * <tr><td> Allow consumers to decide when to unblock synchronous producers.
+ * </table>
+ */
+public interface BatchSynchQueue<E> extends BlockingQueue<E>
+{
+ /**
+ * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the
+ * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}.
+ *
+ * @param e The data element to put into the queue.
+ *
+ * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting
+ * on its entry in the queue being consumed.
+ * @throws SynchException If a consumer encounters an error whilst processing the data element.
+ */
+ public void tryPut(E e) throws InterruptedException, SynchException;
+
+ /**
+ * Takes all available data items from the queue or blocks until some become available. The returned items
+ * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
+ * producers, where the producers are still blocked.
+ *
+ * @param c The collection to drain the data items into.
+ * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
+ *
+ * @return A count of the number of elements that were drained from the queue.
+ */
+ public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock);
+
+ /**
+ * Takes up to maxElements available data items from the queue or blocks until some become available. The returned
+ * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
+ * producers, where the producers are still blocked.
+ *
+ * @param c The collection to drain the data items into.
+ * @param maxElements The maximum number of elements to drain.
+ * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
+ *
+ * @return A count of the number of elements that were drained from the queue.
+ */
+ public SynchRef drainTo(Collection<SynchRecord<E>> c, int maxElements, boolean unblock);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java
new file mode 100644
index 0000000000..4564b1d686
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java
@@ -0,0 +1,834 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data.
+ * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being
+ * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and
+ * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is
+ * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch.
+ * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls.
+ *
+ * <p/>Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete
+ * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract},
+ * {@link #getBufferCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to implement
+ * the buffer other than by a queue, for example, by using an array.
+ *
+ * <p/>Normal queue methods to work asynchronously.
+ * <p/>Put, take and drain methods from the BlockingQueue interface work synchronously but unblock producers immediately
+ * when their data is taken.
+ * <p/>The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the
+ * option to keep producers blocked until the consumer decides to release them.
+ *
+ * <p/>Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to
+ * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency
+ * gain of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io)
+ * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the
+ * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an
+ * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to
+ * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+public abstract class BatchSynchQueueBase<E> extends AbstractQueue<E> implements BatchSynchQueue<E>
+{
+ /** Used for logging. */
+ private static final Logger log = LoggerFactory.getLogger(BatchSynchQueueBase.class);
+
+ /** Holds a reference to the queue implementation that holds the buffer. */
+ Queue<SynchRecordImpl<E>> buffer;
+
+ /** Holds the number of items in the queue */
+ private int count;
+
+ /** Main lock guarding all access */
+ private ReentrantLock lock;
+
+ /** Condition for waiting takes */
+ private Condition notEmpty;
+
+ /** Condition for waiting puts */
+ private Condition notFull;
+
+ /**
+ * Creates a batch synch queue without fair thread scheduling.
+ */
+ public BatchSynchQueueBase()
+ {
+ this(false);
+ }
+
+ /**
+ * Ensures that the underlying buffer implementation is created.
+ *
+ * @param fair <tt>true</tt> if fairness is to be applied to threads waiting to access the buffer.
+ */
+ public BatchSynchQueueBase(boolean fair)
+ {
+ buffer = this.createQueue();
+
+ // Create the buffer lock with the fairness flag set accordingly.
+ lock = new ReentrantLock(fair);
+
+ // Create the non-empty and non-full condition monitors on the buffer lock.
+ notEmpty = lock.newCondition();
+ notFull = lock.newCondition();
+ }
+
+ /**
+ * Returns an iterator over the elements contained in this collection.
+ *
+ * @return An iterator over the elements contained in this collection.
+ */
+ public Iterator<E> iterator()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Returns the number of elements in this collection. If the collection contains more than
+ * <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
+ *
+ * @return The number of elements in this collection.
+ */
+ public int size()
+ {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ return count;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Inserts the specified element into this queue, if possible. When using queues that may impose insertion
+ * restrictions (for example capacity bounds), method <tt>offer</tt> is generally preferable to method
+ * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception.
+ *
+ * @param e The element to insert.
+ *
+ * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt>
+ */
+ public boolean offer(E e)
+ {
+ if (e == null)
+ {
+ throw new NullPointerException();
+ }
+
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ return insert(e, false);
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to
+ * become available.
+ *
+ * @param e The element to add.
+ * @param timeout How long to wait before giving up, in units of <tt>unit</tt>
+ * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
+ *
+ * @return <tt>true</tt> if successful, or <tt>false</tt> if the specified waiting time elapses before space is
+ * available.
+ *
+ * @throws InterruptedException If interrupted while waiting.
+ * @throws NullPointerException If the specified element is <tt>null</tt>.
+ */
+ public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
+ {
+ if (e == null)
+ {
+ throw new NullPointerException();
+ }
+
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+
+ long nanos = unit.toNanos(timeout);
+
+ try
+ {
+ do
+ {
+ if (insert(e, false))
+ {
+ return true;
+ }
+
+ try
+ {
+ nanos = notFull.awaitNanos(nanos);
+ }
+ catch (InterruptedException ie)
+ {
+ notFull.signal(); // propagate to non-interrupted thread
+ throw ie;
+ }
+ }
+ while (nanos > 0);
+
+ return false;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or <tt>null</tt> if this queue is empty.
+ *
+ * @return The head of this queue, or <tt>null</tt> if this queue is empty.
+ */
+ public E poll()
+ {
+ final ReentrantLock lock = this.lock;
+
+ lock.lock();
+ try
+ {
+ if (count == 0)
+ {
+ return null;
+ }
+
+ E x = extract(true, true).getElement();
+
+ return x;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements
+ * are present on this queue.
+ *
+ * @param timeout How long to wait before giving up, in units of <tt>unit</tt>.
+ * @param unit A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
+ *
+ * @return The head of this queue, or <tt>null</tt> if the specified waiting time elapses before an element is present.
+ *
+ * @throws InterruptedException If interrupted while waiting.
+ */
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException
+ {
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+ try
+ {
+ long nanos = unit.toNanos(timeout);
+
+ do
+ {
+ if (count != 0)
+ {
+ E x = extract(true, true).getElement();
+
+ return x;
+ }
+
+ try
+ {
+ nanos = notEmpty.awaitNanos(nanos);
+ }
+ catch (InterruptedException ie)
+ {
+ notEmpty.signal(); // propagate to non-interrupted thread
+ throw ie;
+ }
+ }
+ while (nanos > 0);
+
+ return null;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Retrieves, but does not remove, the head of this queue, returning <tt>null</tt> if this queue is empty.
+ *
+ * @return The head of this queue, or <tt>null</tt> if this queue is empty.
+ */
+ public E peek()
+ {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ return peekAtBufferHead();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints)
+ * accept without blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic limit.
+ *
+ * <p>Note that you <em>cannot</em> always tell if an attempt to <tt>add</tt> an element will succeed by
+ * inspecting <tt>remainingCapacity</tt> because it may be the case that another thread is about to <tt>put</tt>
+ * or <tt>take</tt> an element.
+ *
+ * @return The remaining capacity.
+ */
+ public int remainingCapacity()
+ {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ return getBufferCapacity() - count;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Adds the specified element to this queue, waiting if necessary for space to become available.
+ *
+ * <p/>This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised
+ * this method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these
+ * exceptions.
+ *
+ * @param e The element to add.
+ *
+ * @throws InterruptedException If interrupted while waiting.
+ */
+ public void put(E e) throws InterruptedException
+ {
+ try
+ {
+ tryPut(e);
+ }
+ catch (SynchException ex)
+ {
+ // This exception is deliberately ignored. See the method comment for information about this.
+ }
+ }
+
+ /**
+ * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the
+ * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}.
+ *
+ * @param e The data element to put into the queue. Cannot be null.
+ *
+ * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting
+ * on its entry in the queue being consumed.
+ * @throws SynchException If a consumer encounters an error whilst processing the data element.
+ */
+ public void tryPut(E e) throws InterruptedException, SynchException
+ {
+ if (e == null)
+ {
+ throw new NullPointerException();
+ }
+
+ // final Queue<E> items = this.buffer;
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+
+ try
+ {
+ while (count == getBufferCapacity())
+ {
+ // Release the lock and wait until the queue is not full.
+ notFull.await();
+ }
+ }
+ catch (InterruptedException ie)
+ {
+ notFull.signal(); // propagate to non-interrupted thread
+ throw ie;
+ }
+
+ // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block
+ // the producer until its data is taken.
+ insert(e, true);
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, waiting if no elements are present on this queue.
+ * Any producer that has its data element taken by this call will be immediately unblocked. To keep the
+ * producer blocked whilst taking just a single item, use the
+ * {@link #drainTo(java.util.Collection<org.apache.qpid.util.concurrent.SynchRecord<E>>, int, boolean)}
+ * method. There is no take method to do that because there is not usually any advantage in a synchronous hand
+ * off design that consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption
+ * latencies accross many producers where possible.
+ *
+ * @return The head of this queue.
+ *
+ * @throws InterruptedException if interrupted while waiting.
+ */
+ public E take() throws InterruptedException
+ {
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+
+ try
+ {
+ try
+ {
+ while (count == 0)
+ {
+ // Release the lock and wait until the queue becomes non-empty.
+ notEmpty.await();
+ }
+ }
+ catch (InterruptedException ie)
+ {
+ notEmpty.signal(); // propagate to non-interrupted thread
+ throw ie;
+ }
+
+ // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is
+ // not full, and unblock the producer that owns the data item that is taken.
+ E x = extract(true, true).getElement();
+
+ return x;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Removes all available elements from this queue and adds them into the given collection. This operation may be
+ * more efficient than repeatedly polling this queue. A failure encountered while attempting to <tt>add</tt> elements
+ * to collection <tt>c</tt> may result in elements being in neither, either or both collections when the associated
+ * exception is thrown. Attempts to drain a queue to itself result in <tt>IllegalArgumentException</tt>. Further,
+ * the behavior of this operation is undefined if the specified collection is modified while the operation is in
+ * progress.
+ *
+ * @param objects The collection to transfer elements into.
+ *
+ * @return The number of elements transferred.
+ *
+ * @throws NullPointerException If objects is null.
+ * @throws IllegalArgumentException If objects is this queue.
+ */
+ public int drainTo(Collection<? super E> objects)
+ {
+ return drainTo(objects, -1);
+ }
+
+ /**
+ * Removes at most the given number of available elements from this queue and adds them into the given collection.
+ * A failure encountered while attempting to <tt>add</tt> elements to collection <tt>c</tt> may result in elements
+ * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue
+ * to itself result in <tt>IllegalArgumentException</tt>. Further, the behavior of this operation is undefined if
+ * the specified collection is modified while the operation is in progress.
+ *
+ * @param objects The collection to transfer elements into.
+ * @param maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning
+ * all elements.
+ *
+ * @return The number of elements transferred.
+ *
+ * @throws NullPointerException If c is null.
+ * @throws IllegalArgumentException If c is this queue.
+ */
+ public int drainTo(Collection<? super E> objects, int maxElements)
+ {
+ if (objects == null)
+ {
+ throw new NullPointerException();
+ }
+
+ if (objects == this)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ // final Queue<E> items = this.buffer;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ int n = 0;
+
+ for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
+ {
+ // Take items from the queue, do unblock the producers, but don't send not full signals yet.
+ objects.add(extract(true, false).getElement());
+ }
+
+ if (n > 0)
+ {
+ // count -= n;
+ notFull.signalAll();
+ }
+
+ return n;
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Takes all available data items from the queue or blocks until some become available. The returned items
+ * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
+ * producers, where the producers are still blocked.
+ *
+ * @param c The collection to drain the data items into.
+ * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
+ *
+ * @return A count of the number of elements that were drained from the queue.
+ */
+ public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock)
+ {
+ return drainTo(c, -1, unblock);
+ }
+
+ /**
+ * Takes up to maxElements available data items from the queue or blocks until some become available. The returned
+ * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
+ * producers, where the producers are still blocked.
+ *
+ * @param coll The collection to drain the data items into.
+ * @param maxElements The maximum number of elements to drain.
+ * @param unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
+ *
+ * @return A count of the number of elements that were drained from the queue.
+ */
+ public SynchRef drainTo(Collection<SynchRecord<E>> coll, int maxElements, boolean unblock)
+ {
+ if (coll == null)
+ {
+ throw new NullPointerException();
+ }
+
+ // final Queue<E> items = this.buffer;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+
+ try
+ {
+ int n = 0;
+
+ for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
+ {
+ // Extract the next record from the queue, don't signall the not full condition yet and release
+ // producers depending on whether the caller wants to or not.
+ coll.add(extract(false, unblock));
+ }
+
+ if (n > 0)
+ {
+ // count -= n;
+ notFull.signalAll();
+ }
+
+ return new SynchRefImpl(n, coll);
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * This abstract method should be overriden to return an empty queue. Different implementations of producer
+ * consumer buffers can control the order in which data is accessed using different queue implementations.
+ * This method allows the type of queue to be abstracted out of this class and to be supplied by concrete
+ * implementations.
+ *
+ * @return An empty queue.
+ */
+ protected abstract <T> Queue<T> createQueue();
+
+ /**
+ * Insert element into the queue, then possibly signal that the queue is not empty and block the producer
+ * on the element until permission to procede is given.
+ *
+ * <p/>If the producer is to be blocked then the lock must be released first, otherwise no other process
+ * will be able to get access to the queue. Hence, unlock and block are always set together.
+ *
+ * <p/>Call only when holding the global lock.
+ *
+ * @param unlockAndBlock <tt>true</tt>If the global queue lock should be released and the producer should be blocked.
+ *
+ * @return <tt>true</tt> if the operation succeeded, <tt>false</tt> otherwise. If the result is <tt>true</tt> this
+ * method may not return straight away, but only after the producer is unblocked by having its data
+ * consumed if the unlockAndBlock flag is set. In the false case the method will return straight away, no
+ * matter what value the unlockAndBlock flag has, leaving the global lock on.
+ */
+ protected boolean insert(E x, boolean unlockAndBlock)
+ {
+ // Create a new record for the data item.
+ SynchRecordImpl<E> record = new SynchRecordImpl<E>(x);
+
+ boolean result = buffer.offer(record);
+
+ if (result)
+ {
+ count++;
+
+ // Tell any waiting consumers that the queue is not empty.
+ notEmpty.signal();
+
+ if (unlockAndBlock)
+ {
+ // Allow other threads to read/write the queue.
+ lock.unlock();
+
+ // Wait until a consumer takes this data item.
+ record.waitForConsumer();
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Extract element at current take position, advance, and signal.
+ *
+ * <p/>Call only when holding lock.
+ */
+ protected SynchRecordImpl<E> extract(boolean unblock, boolean signal)
+ {
+ SynchRecordImpl<E> result = buffer.remove();
+ count--;
+
+ if (signal)
+ {
+ notFull.signal();
+ }
+
+ if (unblock)
+ {
+ result.releaseImmediately();
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned.
+ *
+ * <p/>Call only when holding lock.
+ *
+ * @return The maximum capacity of the buffer.
+ */
+ protected int getBufferCapacity()
+ {
+ if (buffer instanceof Capacity)
+ {
+ return ((Capacity) buffer).getCapacity();
+ }
+ else
+ {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Return the head element from the buffer.
+ *
+ * <p/>Call only when holding lock.
+ *
+ * @return The head element from the buffer.
+ */
+ protected E peekAtBufferHead()
+ {
+ return buffer.peek().getElement();
+ }
+
+ public class SynchRefImpl implements SynchRef
+ {
+ /** Holds the number of synch records associated with this reference. */
+ int numRecords;
+
+ /** Holds a reference to the collection of synch records managed by this. */
+ Collection<SynchRecord<E>> records;
+
+ public SynchRefImpl(int n, Collection<SynchRecord<E>> records)
+ {
+ this.numRecords = n;
+ this.records = records;
+ }
+
+ public int getNumRecords()
+ {
+ return numRecords;
+ }
+
+ /**
+ * Any producers that have had their data elements taken from the queue but have not been unblocked are unblocked
+ * when this method is called. The exception to this is producers that have had their data put back onto the queue
+ * by a consumer. Producers that have had exceptions for their data items registered by consumers will be unblocked
+ * but will not return from their put call normally, but with an exception instead.
+ */
+ public void unblockProducers()
+ {
+ log.debug("public void unblockProducers(): called");
+
+ if (records != null)
+ {
+ for (SynchRecord<E> record : records)
+ {
+ // This call takes account of items that have already been released, are to be requeued or are in
+ // error.
+ record.releaseImmediately();
+ }
+ }
+
+ records = null;
+ }
+ }
+
+ /**
+ * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows
+ * the producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when
+ * its data cannot be consumed.
+ */
+ public class SynchRecordImpl<E> implements SynchRecord<E>
+ {
+ /** A boolean latch that determines when the producer for this data item will be allowed to continue. */
+ BooleanLatch latch = new BooleanLatch();
+
+ /** The data element associated with this item. */
+ E element;
+
+ /**
+ * Create a new synch record.
+ *
+ * @param e The data element that the record encapsulates.
+ */
+ public SynchRecordImpl(E e)
+ {
+ // Keep the data element.
+ element = e;
+ }
+
+ /**
+ * Waits until the producer is given permission to proceded by a consumer.
+ */
+ public void waitForConsumer()
+ {
+ latch.await();
+ }
+
+ /**
+ * Gets the data element contained by this record.
+ *
+ * @return The data element contained by this record.
+ */
+ public E getElement()
+ {
+ return element;
+ }
+
+ /**
+ * Immediately releases the producer of this data record. Consumers can bring the synchronization time of
+ * producers to a minimum by using this method to release them at the earliest possible moment when batch
+ * consuming records from sychronized producers.
+ */
+ public void releaseImmediately()
+ {
+ // Check that the record has not already been released, is in error or is to be requeued.
+ latch.signal();
+
+ // Propagate errors to the producer.
+
+ // Requeue items to be requeued.
+ }
+
+ /**
+ * Tells the synch queue to put this element back onto the queue instead of releasing its producer.
+ * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or
+ * the {@link #releaseImmediately()} method.
+ *
+ * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
+ * element has already been unblocked.
+ */
+ public void reQueue()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Tells the synch queue to raise an exception with this elements producer. The exception is not raised
+ * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the
+ * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is
+ * raised on the producer.
+ *
+ * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used
+ * because the exception is to be passed onto a different thread.
+ *
+ * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
+ * element has already been unblocked.
+ *
+ * @param e The exception to raise on the producer.
+ */
+ public void inError(Exception e)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java
new file mode 100644
index 0000000000..0e4a07594f
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java
@@ -0,0 +1,128 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.concurrent.locks.AbstractQueuedSynchronizer;
+
+/**
+ * A BooleanLatch is like a set of traffic lights, where threads can wait at a red light until another thread gives
+ * the green light. When threads arrive at the latch it is initially red. They queue up until the green signal is
+ * given, at which point they can all acquire the latch in shared mode and continue to run concurrently. Once the latch
+ * is signalled it cannot be reset to red again.
+ *
+ * <p/> The latch uses a {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} to implement its synchronization.
+ * This has two internal states, 0 which means that the latch is blocked, and 1 which means that the latch is open.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Block threads until a go signal is given.
+ * </table>
+ *
+ * @todo Might be better to use a countdown latch to count down from 1. Its await method can throw interrupted
+ * exception which makes the possibility of interruption more explicit, and provides a reminder to recheck the
+ * latch condition before continuing.
+ */
+public class BooleanLatch
+{
+ /** Holds the synchronizer that provides the thread queueing synchronization. */
+ private final Sync sync = new Sync();
+
+ /**
+ * Tests whether or not the latch has been signalled, that is to say that, the light is green.
+ *
+ * <p/>This method is non-blocking.
+ *
+ * @return <tt>true</tt> if the latch may be acquired; the light is green.
+ */
+ public boolean isSignalled()
+ {
+ return sync.isSignalled();
+ }
+
+ /**
+ * Waits on the latch until the signal is given and the light is green. If the light is already green then the
+ * latch will be acquired and the thread will not have to wait.
+ *
+ * <p/>This method will block until the go signal is given or the thread is otherwise interrupted. Before carrying
+ * out any processing threads that return from this method should confirm that the go signal has really been given
+ * on this latch by calling the {@link #isSignalled()} method.
+ */
+ public void await()
+ {
+ sync.acquireShared(1);
+ }
+
+ /**
+ * Releases any threads currently waiting on the latch. This flips the light to green allowing any threads that
+ * were waiting for this condition to now run.
+ *
+ * <p/>This method is non-blocking.
+ */
+ public void signal()
+ {
+ sync.releaseShared(1);
+ }
+
+ /**
+ * Implements a thread queued synchronizer. The internal state 0 means that the queue is blocked and the internl
+ * state 1 means that the queue is released and that all waiting threads can acquire the synchronizer in shared
+ * mode.
+ */
+ private static class Sync extends AbstractQueuedSynchronizer
+ {
+ /**
+ * Attempts to acquire this synchronizer in shared mode. It may be acquired once it has been released.
+ *
+ * @param ignore This parameter is ignored.
+ *
+ * @return 1 if the shared acquisition succeeds and -1 if it fails.
+ */
+ protected int tryAcquireShared(int ignore)
+ {
+ return isSignalled() ? 1 : -1;
+ }
+
+ /**
+ * Releases the synchronizer, setting its internal state to 1.
+ *
+ * @param ignore This parameter is ignored.
+ *
+ * @return <tt>true</tt> always.
+ */
+ protected boolean tryReleaseShared(int ignore)
+ {
+ setState(1);
+
+ return true;
+ }
+
+ /**
+ * Tests if the synchronizer is signalled. It is signalled when its internal state it 1.
+ *
+ * @return <tt>true</tt> if the internal state is 1, <tt>false</tt> otherwise.
+ */
+ boolean isSignalled()
+ {
+ return getState() != 0;
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java
new file mode 100644
index 0000000000..a97ce0e172
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java
@@ -0,0 +1,35 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * An interface exposed by data structures that have a maximum capacity.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Report the maximum capacity.
+ * </table>
+ */
+public interface Capacity
+{
+ public int getCapacity();
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.java
new file mode 100644
index 0000000000..bbd1722677
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.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.util.concurrent;
+
+
+/**
+ * Condition
+ *
+ */
+
+public class Condition
+{
+
+ private boolean value = false;
+
+ public synchronized void set()
+ {
+ value = true;
+ notifyAll();
+ }
+
+ public synchronized boolean get(long timeout) throws InterruptedException
+ {
+ if (!value)
+ {
+ wait(timeout);
+ }
+
+ return value;
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java
new file mode 100644
index 0000000000..bc63eb0353
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java
@@ -0,0 +1,50 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.Queue;
+
+/**
+ * SynchBuffer completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying
+ * queue as an array. This uses FIFO ordering for the queue but restricts the maximum size of the queue to a fixed
+ * amount. It also has the advantage that, as the buffer does not grow and shrink dynamically, memory for the buffer
+ * is allocated up front and does not create garbage during the operation of the queue.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide array based FIFO queue to create a batch synched queue around.
+ * </table>
+ *
+ * @todo Write an array based buffer implementation that implements Queue.
+ */
+public class SynchBuffer<E> extends BatchSynchQueueBase<E>
+{
+ /**
+ * Returns an empty queue, implemented as an array.
+ *
+ * @return An empty queue, implemented as an array.
+ */
+ protected <T> Queue<T> createQueue()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java
new file mode 100644
index 0000000000..99a83f96cd
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java
@@ -0,0 +1,52 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions
+ * back from the consumers of a {@link BatchSynchQueue} to producers. The underlying exception should be retrieved from
+ * the {@link #getCause} method.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Encapsulate a data element and exception.
+ * </table>
+ */
+public class SynchException extends Exception
+{
+ /** Holds the data element that is in error. */
+ Object element;
+
+ /**
+ * Creates a new BaseApplicationException object.
+ *
+ * @param message The exception message.
+ * @param cause The underlying throwable cause. This may be null.
+ */
+ public SynchException(String message, Throwable cause, Object element)
+ {
+ super(message, cause);
+
+ // Keep the data element that was in error.
+ this.element = element;
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java
new file mode 100644
index 0000000000..95833f398a
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java
@@ -0,0 +1,48 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.LinkedList;
+import java.util.Queue;
+
+/**
+ * SynchQueue completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying
+ * queue as a linked list. This uses FIFO ordering for the queue and allows the queue to grow to accomodate more
+ * elements as needed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide linked list FIFO queue to create a batch synched queue around.
+ * </table>
+ */
+public class SynchQueue<E> extends BatchSynchQueueBase<E>
+{
+ /**
+ * Returns an empty queue, implemented as a linked list.
+ *
+ * @return An empty queue, implemented as a linked list.
+ */
+ protected <T> Queue<T> createQueue()
+ {
+ return new LinkedList<T>();
+ }
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java
new file mode 100644
index 0000000000..fd740c20cd
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java
@@ -0,0 +1,74 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data
+ * item to be put back on the queue without unblocking its producer, or to send exceptions to the producer.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Get the underlying data element.
+ * <tr><td> Put the data element back on the queue without unblocking its producer.
+ * <tr><td> Send and exception to the data elements producer.
+ * </table>
+ */
+public interface SynchRecord<E>
+{
+ /**
+ * Gets the data element contained by this record.
+ *
+ * @return The data element contained by this record.
+ */
+ public E getElement();
+
+ /**
+ * Tells the synch queue to put this element back onto the queue instead of releasing its producer.
+ * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method.
+ *
+ * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element
+ * has already been unblocked.
+ */
+ public void reQueue();
+
+ /**
+ * Immediately releases the producer of this data record. Consumers can bring the synchronization time of
+ * producers to a minimum by using this method to release them at the earliest possible moment when batch
+ * consuming records from sychronized producers.
+ */
+ public void releaseImmediately();
+
+ /**
+ * Tells the synch queue to raise an exception with this elements producer. The exception is not raised immediately
+ * but upon calling the {@link SynchRef#unblockProducers()} method. The exception will be wrapped in a
+ * {@link SynchException} before it is raised on the producer.
+ *
+ * <p/>This method is unusual in that it accepts an exception as an argument. This is non-standard but is used
+ * because the exception is to be passed onto a different thread.
+ *
+ * <p/>This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element
+ * has already been unblocked.
+ *
+ * @param e The exception to raise on the producer.
+ */
+ public void inError(Exception e);
+}
diff --git a/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java
new file mode 100644
index 0000000000..efe2344c06
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java
@@ -0,0 +1,51 @@
+package org.apache.qpid.util.concurrent;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue},
+ * allowing call-backs to be made against the synchronizing strucutre. It allows the consumer to communicate when it
+ * wants producers that have their data taken to be unblocked.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Report number of records returned by a taking operation.
+ * <tr><td> Provide call-back to release producers of taken records.
+ * </table>
+ */
+public interface SynchRef
+{
+ /**
+ * Reports the number of records taken by the take or drain operation.
+ *
+ * @return The number of records taken by the take or drain operation.
+ */
+ public int getNumRecords();
+
+ /**
+ * Any producers that have had their data elements taken from the queue but have not been unblocked are
+ * unblocked when this method is called. The exception to this is producers that have had their data put back
+ * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers
+ * will be unblocked but will not return from their put call normally, but with an exception instead.
+ */
+ public void unblockProducers();
+}
diff --git a/RC9/qpid/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert b/RC9/qpid/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert
new file mode 100644
index 0000000000..e6702108e6
--- /dev/null
+++ b/RC9/qpid/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert
Binary files differ
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java b/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
new file mode 100644
index 0000000000..b93dc46741
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
@@ -0,0 +1,396 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.mina.SocketIOTest;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.CloseFuture;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.IoConnector;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+
+public class IOWriterClient implements Runnable
+{
+ private static final Logger _logger = LoggerFactory.getLogger(IOWriterClient.class);
+
+ public static int DEFAULT_TEST_SIZE = 2;
+
+ private IoSession _session;
+
+ private long _startTime;
+
+ private long[] _chunkTimes;
+
+ public int _chunkCount = 200000;
+
+ private int _chunkSize = 1024;
+
+ private CountDownLatch _notifier;
+
+ private int _maximumWriteQueueLength;
+
+ static public int _PORT = IOWriterServer._PORT;
+
+ public void run()
+ {
+ _logger.info("Starting to send " + _chunkCount + " buffers of " + _chunkSize + "B");
+ _startTime = System.currentTimeMillis();
+ _notifier = new CountDownLatch(1);
+
+ for (int i = 0; i < _chunkCount; i++)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(_chunkSize, false);
+ byte check = (byte) (i % 128);
+ buf.put(check);
+ buf.fill((byte) 88, buf.remaining());
+ buf.flip();
+
+ _session.write(buf);
+ }
+
+ long _sentall = System.currentTimeMillis();
+ long _receivedall = _sentall;
+ try
+ {
+ _logger.info("All buffers sent; waiting for receipt from server");
+ _notifier.await();
+ _receivedall = System.currentTimeMillis();
+ }
+ catch (InterruptedException e)
+ {
+ //Ignore
+ }
+ _logger.info("Completed");
+ _logger.info("Total time waiting for server after last write: " + (_receivedall - _sentall));
+
+ long totalTime = System.currentTimeMillis() - _startTime;
+
+ _logger.info("Total time: " + totalTime);
+ _logger.info("MB per second: " + (int) ((1.0 * _chunkSize * _chunkCount) / totalTime));
+ long lastChunkTime = _startTime;
+ double average = 0;
+ for (int i = 0; i < _chunkTimes.length; i++)
+ {
+ if (i == 0)
+ {
+ average = _chunkTimes[i] - _startTime;
+ }
+ else
+ {
+ long delta = _chunkTimes[i] - lastChunkTime;
+ if (delta != 0)
+ {
+ average = (average + delta) / 2;
+ }
+ }
+ lastChunkTime = _chunkTimes[i];
+ }
+ _logger.info("Average chunk time: " + average + "ms");
+ _logger.info("Maximum WriteRequestQueue size: " + _maximumWriteQueueLength);
+
+ CloseFuture cf = _session.close();
+ _logger.info("Closing session");
+ cf.join();
+ }
+
+ private class WriterHandler extends IoHandlerAdapter
+ {
+ private int _chunksReceived = 0;
+
+ private int _partialBytesRead = 0;
+
+ private byte _partialCheckNumber;
+
+ private int _totalBytesReceived = 0;
+
+ private int _receivedCount = 0;
+ private int _sentCount = 0;
+ private static final String DEFAULT_READ_BUFFER = "262144";
+ private static final String DEFAULT_WRITE_BUFFER = "262144";
+
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ IoFilterChain chain = session.getFilterChain();
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
+
+ writefilter.attach(chain);
+ }
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ _maximumWriteQueueLength = Math.max(session.getScheduledWriteRequests(), _maximumWriteQueueLength);
+
+ if (_logger.isDebugEnabled())
+ {
+ ++_sentCount;
+ if (_sentCount % 1000 == 0)
+ {
+ _logger.debug("Sent count " + _sentCount + ":WQueue" + session.getScheduledWriteRequests());
+
+ }
+ }
+ }
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ ++_receivedCount;
+
+ if (_receivedCount % 1000 == 0)
+ {
+ _logger.debug("Receieved count " + _receivedCount);
+ }
+ }
+
+ ByteBuffer result = (ByteBuffer) message;
+ _totalBytesReceived += result.remaining();
+ int size = result.remaining();
+ long now = System.currentTimeMillis();
+ if (_partialBytesRead > 0)
+ {
+ int offset = _chunkSize - _partialBytesRead;
+ if (size >= offset)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ result.position(offset);
+ }
+ else
+ {
+ // have not read even one chunk, including the previous partial bytes
+ _partialBytesRead += size;
+ return;
+ }
+ }
+
+
+ int chunkCount = result.remaining() / _chunkSize;
+
+ for (int i = 0; i < chunkCount; i++)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ byte check = result.get();
+ _logger.debug("Check number " + check + " read");
+ if (check != (byte) ((_chunksReceived - 1) % 128))
+ {
+ _logger.error("Check number " + check + " read when expected " + (_chunksReceived % 128));
+ }
+ _logger.debug("Chunk times recorded");
+
+ try
+ {
+ result.skip(_chunkSize - 1);
+ }
+ catch (IllegalArgumentException e)
+ {
+ _logger.error("Position was: " + result.position());
+ _logger.error("Tried to skip to: " + (_chunkSize * i));
+ _logger.error("limit was; " + result.limit());
+ }
+ }
+ _logger.debug("Chunks received now " + _chunksReceived);
+ _logger.debug("Bytes received: " + _totalBytesReceived);
+ _partialBytesRead = result.remaining();
+
+ if (_partialBytesRead > 0)
+ {
+ _partialCheckNumber = result.get();
+ }
+
+
+ if (_chunksReceived >= _chunkCount)
+ {
+ _notifier.countDown();
+ }
+
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void startWriter() throws IOException, InterruptedException
+ {
+
+ _maximumWriteQueueLength = 0;
+
+ IoConnector ioConnector = null;
+
+ if (Boolean.getBoolean("multinio"))
+ {
+ _logger.warn("Using MultiThread NIO");
+ ioConnector = new org.apache.mina.transport.socket.nio.MultiThreadSocketConnector();
+ }
+ else
+ {
+ _logger.warn("Using MINA NIO");
+ ioConnector = new org.apache.mina.transport.socket.nio.SocketConnector();
+ }
+
+ SocketSessionConfig scfg = (SocketSessionConfig) ioConnector.getDefaultConfig().getSessionConfig();
+ scfg.setTcpNoDelay(true);
+ scfg.setSendBufferSize(32768);
+ scfg.setReceiveBufferSize(32768);
+
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+
+
+ final InetSocketAddress address = new InetSocketAddress("localhost", _PORT);
+ _logger.info("Attempting connection to " + address);
+
+ //Old mina style
+// ioConnector.setHandler(new WriterHandler());
+// ConnectFuture future = ioConnector.connect(address);
+ ConnectFuture future = ioConnector.connect(address, new WriterHandler());
+ // wait for connection to complete
+ future.join();
+ _logger.info("Connection completed");
+ // we call getSession which throws an IOException if there has been an error connecting
+ _session = future.getSession();
+
+ _chunkTimes = new long[_chunkCount];
+ Thread t = new Thread(this);
+ t.start();
+ t.join();
+ _logger.info("Test Complete");
+ }
+
+
+ public void test1k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 1k test");
+ _chunkSize = 1024;
+ startWriter();
+ }
+
+
+ public void test2k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 2k test");
+ _chunkSize = 2048;
+ startWriter();
+ }
+
+
+ public void test4k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 4k test");
+ _chunkSize = 4096;
+ startWriter();
+ }
+
+
+ public void test8k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 8k test");
+ _chunkSize = 8192;
+ startWriter();
+ }
+
+
+ public void test16k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 16k test");
+ _chunkSize = 16384;
+ startWriter();
+ }
+
+
+ public void test32k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 32k test");
+ _chunkSize = 32768;
+ startWriter();
+ }
+
+
+ public static int getIntArg(String[] args, int index, int defaultValue)
+ {
+ if (args.length > index)
+ {
+ try
+ {
+ return Integer.parseInt(args[index]);
+ }
+ catch (NumberFormatException e)
+ {
+ //Do nothing
+ }
+ }
+ return defaultValue;
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException
+ {
+ _PORT = getIntArg(args, 0, _PORT);
+
+ int test = getIntArg(args, 1, DEFAULT_TEST_SIZE);
+
+ IOWriterClient w = new IOWriterClient();
+ w._chunkCount = getIntArg(args, 2, w._chunkCount);
+ switch (test)
+ {
+ case 0:
+ w.test1k();
+ w.test2k();
+ w.test4k();
+ w.test8k();
+ w.test16k();
+ w.test32k();
+ break;
+ case 1:
+ w.test1k();
+ break;
+ case 2:
+ w.test2k();
+ break;
+ case 4:
+ w.test4k();
+ break;
+ case 8:
+ w.test8k();
+ break;
+ case 16:
+ w.test16k();
+ break;
+ case 32:
+ w.test32k();
+ break;
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java b/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
new file mode 100644
index 0000000000..423e98c67b
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.mina.SocketIOTest;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/** Tests MINA socket performance. This acceptor simply reads data from the network and writes it back again. */
+public class IOWriterServer
+{
+ private static final Logger _logger = LoggerFactory.getLogger(IOWriterServer.class);
+
+ static public int _PORT = 9999;
+
+ private static final String DEFAULT_READ_BUFFER = "262144";
+ private static final String DEFAULT_WRITE_BUFFER = "262144";
+
+
+ private static class TestHandler extends IoHandlerAdapter
+ {
+ private int _sentCount = 0;
+
+ private int _bytesSent = 0;
+
+ private int _receivedCount = 0;
+
+ public void sessionCreated(IoSession ioSession) throws java.lang.Exception
+ {
+ IoFilterChain chain = ioSession.getFilterChain();
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
+
+ writefilter.attach(chain);
+
+ }
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ ((ByteBuffer) message).acquire();
+ session.write(message);
+
+ if (_logger.isDebugEnabled())
+ {
+ _bytesSent += ((ByteBuffer) message).remaining();
+
+ _sentCount++;
+
+ if (_sentCount % 1000 == 0)
+ {
+ _logger.debug("Bytes sent: " + _bytesSent);
+ }
+ }
+ }
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ ++_receivedCount;
+
+ if (_receivedCount % 1000 == 0)
+ {
+ _logger.debug("Receieved count " + _receivedCount);
+ }
+ }
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void startAcceptor() throws IOException
+ {
+ IoAcceptor acceptor;
+ if (Boolean.getBoolean("multinio"))
+ {
+ _logger.warn("Using MultiThread NIO");
+ acceptor = new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor();
+ }
+ else
+ {
+ _logger.warn("Using MINA NIO");
+ acceptor = new org.apache.mina.transport.socket.nio.SocketAcceptor();
+ }
+
+
+ SocketSessionConfig sc = (SocketSessionConfig) acceptor.getDefaultConfig().getSessionConfig();
+ sc.setTcpNoDelay(true);
+ sc.setSendBufferSize(32768);
+ sc.setReceiveBufferSize(32768);
+
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+
+ //The old mina style
+// acceptor.setLocalAddress(new InetSocketAddress(_PORT));
+// acceptor.setHandler(new TestHandler());
+// acceptor.bind();
+ acceptor.bind(new InetSocketAddress(_PORT), new TestHandler());
+
+ _logger.info("Bound on port " + _PORT + ":" + _logger.isDebugEnabled());
+ _logger.debug("debug on");
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+
+ if (args.length > 0)
+ {
+ try
+ {
+ _PORT = Integer.parseInt(args[0]);
+ }
+ catch (NumberFormatException e)
+ {
+ //IGNORE so use default port 9999;
+ }
+ }
+
+ IOWriterServer a = new IOWriterServer();
+ a.startAcceptor();
+ }
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java
new file mode 100644
index 0000000000..ef6cd41492
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.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 junit.framework.TestCase;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+
+/**
+ * This test is to ensure that when an AMQException is rethrown that the specified exception is correctly wrapped up.
+ *
+ * There are three cases:
+ * Re-throwing an AMQException
+ * Re-throwing a Subclass of AMQException
+ * Re-throwing a Subclass of AMQException that does not have the default AMQException constructor which will force the
+ * creation of an AMQException.
+ */
+public class AMQExceptionTest extends TestCase
+{
+ /**
+ * Test that an AMQException will be correctly created and rethrown.
+ */
+ public void testRethrowGeneric()
+ {
+ AMQException test = new AMQException(AMQConstant.ACCESS_REFUSED, "refused", new RuntimeException());
+
+ AMQException e = reThrowException(test);
+
+ assertEquals("Exception not of correct class", AMQException.class, e.getClass());
+
+ }
+
+ /**
+ * Test that a subclass of AMQException that has the default constructor will be correctly created and rethrown.
+ */
+ public void testRethrowAMQESubclass()
+ {
+ AMQFrameDecodingException test = new AMQFrameDecodingException(AMQConstant.INTERNAL_ERROR,
+ "Error",
+ new Exception());
+ AMQException e = reThrowException(test);
+
+ assertEquals("Exception not of correct class", AMQFrameDecodingException.class, e.getClass());
+ }
+
+ /**
+ * Test that a subclass of AMQException that doesnot have the default constructor will be correctly rethrown as an
+ * AMQException
+ */
+ public void testRethrowAMQESubclassNoConstructor()
+ {
+ AMQExceptionSubclass test = new AMQExceptionSubclass("Invalid Argument Exception");
+
+ AMQException e = reThrowException(test);
+
+ assertEquals("Exception not of correct class", AMQException.class, e.getClass());
+ }
+
+ /**
+ * Private method to rethrown and validate the basic values of the rethrown
+ * @param test Exception to rethrow
+ * @throws AMQException the rethrown exception
+ */
+ private AMQException reThrowException(AMQException test)
+ {
+ AMQException amqe = test.cloneForCurrentThread();
+
+ assertEquals("Error code does not match.", test.getErrorCode(), amqe.getErrorCode());
+ assertTrue("Exception message does not start as expected.", amqe.getMessage().startsWith(test.getMessage()));
+ assertEquals("Test Exception is not set as the cause", test, amqe.getCause());
+ assertEquals("Cause is not correct", test.getCause(), amqe.getCause().getCause());
+
+ return amqe;
+ }
+
+ /**
+ * Private class that extends AMQException but does not have a default exception.
+ */
+ private class AMQExceptionSubclass extends AMQException
+ {
+
+ public AMQExceptionSubclass(String msg)
+ {
+ super(null, msg, null);
+ }
+ }
+}
+
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java
new file mode 100644
index 0000000000..92e7ce0a80
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing;
+
+import junit.framework.TestCase;
+public class AMQShortStringTest extends TestCase
+{
+
+ public static final AMQShortString HELLO = new AMQShortString("Hello");
+ public static final AMQShortString HELL = new AMQShortString("Hell");
+ public static final AMQShortString GOODBYE = new AMQShortString("Goodbye");
+ public static final AMQShortString GOOD = new AMQShortString("Good");
+ public static final AMQShortString BYE = new AMQShortString("BYE");
+
+ public void testStartsWith()
+ {
+ assertTrue(HELLO.startsWith(HELL));
+
+ assertFalse(HELL.startsWith(HELLO));
+
+ assertTrue(GOODBYE.startsWith(GOOD));
+
+ assertFalse(GOOD.startsWith(GOODBYE));
+ }
+
+ public void testEndWith()
+ {
+ assertFalse(HELL.endsWith(HELLO));
+
+ assertTrue(GOODBYE.endsWith(new AMQShortString("bye")));
+
+ assertFalse(GOODBYE.endsWith(BYE));
+ }
+
+
+ public void testTokenize()
+ {
+ AMQShortString dotSeparatedWords = new AMQShortString("this.is.a.test.with.1.2.3.-numbers-and-then--dashes-");
+ AMQShortStringTokenizer dotTokenizer = dotSeparatedWords.tokenize((byte) '.');
+
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("this"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("is"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("a"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("test"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("with"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 1);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 2);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 3);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ AMQShortString dashString = dotTokenizer.nextToken();
+ assertEquals(new AMQShortString("-numbers-and-then--dashes-"),(dashString));
+
+ AMQShortStringTokenizer dashTokenizer = dashString.tokenize((byte)'-');
+ assertEquals(dashTokenizer.countTokens(), 7);
+
+ AMQShortString[] expectedResults = new AMQShortString[]
+ { AMQShortString.EMPTY_STRING,
+ new AMQShortString("numbers"),
+ new AMQShortString("and"),
+ new AMQShortString("then"),
+ AMQShortString.EMPTY_STRING,
+ new AMQShortString("dashes"),
+ AMQShortString.EMPTY_STRING };
+
+ for(int i = 0; i < 7; i++)
+ {
+ assertTrue(dashTokenizer.hasMoreTokens());
+ assertEquals(dashTokenizer.nextToken(), expectedResults[i]);
+ }
+
+ assertFalse(dotTokenizer.hasMoreTokens());
+ }
+
+
+ public void testEquals()
+ {
+ assertEquals(GOODBYE, new AMQShortString("Goodbye"));
+ assertEquals(new AMQShortString("A"), new AMQShortString("A"));
+ assertFalse(new AMQShortString("A").equals(new AMQShortString("a")));
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java
new file mode 100644
index 0000000000..4fd1f60d69
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import junit.framework.TestCase;
+
+
+public class BasicContentHeaderPropertiesTest extends TestCase
+{
+
+ BasicContentHeaderProperties _testProperties;
+ FieldTable _testTable;
+ String _testString = "This is a test string";
+ int _testint = 666;
+
+ /**
+ * Currently only test setting/getting String, int and boolean props
+ */
+ public BasicContentHeaderPropertiesTest()
+ {
+ _testProperties = new BasicContentHeaderProperties();
+ }
+
+ public void setUp()
+ {
+ _testTable = new FieldTable();
+ _testTable.setString("TestString", _testString);
+ _testTable.setInteger("Testint", _testint);
+ _testProperties = new BasicContentHeaderProperties();
+ _testProperties.setHeaders(_testTable);
+ }
+
+ public void testGetPropertyListSize()
+ {
+ //needs a better test but at least we're exercising the code !
+ // FT length is encoded in an int
+ int expectedSize = EncodingUtils.encodedIntegerLength();
+
+ expectedSize += EncodingUtils.encodedShortStringLength("TestInt");
+ // 1 is for the Encoding Letter. here an 'i'
+ expectedSize += 1 + EncodingUtils.encodedIntegerLength();
+
+ expectedSize += EncodingUtils.encodedShortStringLength("TestString");
+ // 1 is for the Encoding Letter. here an 'S'
+ expectedSize += 1 + EncodingUtils.encodedLongStringLength(_testString);
+
+
+ int size = _testProperties.getPropertyListSize();
+
+ assertEquals(expectedSize, size);
+ }
+
+ public void testGetSetPropertyFlags()
+ {
+ _testProperties.setPropertyFlags(99);
+ assertEquals(99, _testProperties.getPropertyFlags());
+ }
+
+ public void testWritePropertyListPayload()
+ {
+ ByteBuffer buf = ByteBuffer.allocate(300);
+ _testProperties.writePropertyListPayload(buf);
+ }
+
+ public void testPopulatePropertiesFromBuffer() throws Exception
+ {
+ ByteBuffer buf = ByteBuffer.allocate(300);
+ _testProperties.populatePropertiesFromBuffer(buf, 99, 99);
+ }
+
+ public void testSetGetContentType()
+ {
+ String contentType = "contentType";
+ _testProperties.setContentType(contentType);
+ assertEquals(contentType, _testProperties.getContentTypeAsString());
+ }
+
+ public void testSetGetEncoding()
+ {
+ String encoding = "encoding";
+ _testProperties.setEncoding(encoding);
+ assertEquals(encoding, _testProperties.getEncodingAsString());
+ }
+
+ public void testSetGetHeaders()
+ {
+ _testProperties.setHeaders(_testTable);
+ assertEquals(_testTable, _testProperties.getHeaders());
+ }
+
+ public void testSetGetDeliveryMode()
+ {
+ byte deliveryMode = 1;
+ _testProperties.setDeliveryMode(deliveryMode);
+ assertEquals(deliveryMode, _testProperties.getDeliveryMode());
+ }
+
+ public void testSetGetPriority()
+ {
+ byte priority = 1;
+ _testProperties.setPriority(priority);
+ assertEquals(priority, _testProperties.getPriority());
+ }
+
+ public void testSetGetCorrelationId()
+ {
+ String correlationId = "correlationId";
+ _testProperties.setCorrelationId(correlationId);
+ assertEquals(correlationId, _testProperties.getCorrelationIdAsString());
+ }
+
+ public void testSetGetReplyTo()
+ {
+ String replyTo = "replyTo";
+ _testProperties.setReplyTo(replyTo);
+ assertEquals(replyTo, _testProperties.getReplyToAsString());
+ }
+
+ public void testSetGetExpiration()
+ {
+ long expiration = 999999999;
+ _testProperties.setExpiration(expiration);
+ assertEquals(expiration, _testProperties.getExpiration());
+ }
+
+ public void testSetGetMessageId()
+ {
+ String messageId = "messageId";
+ _testProperties.setMessageId(messageId);
+ assertEquals(messageId, _testProperties.getMessageIdAsString());
+ }
+
+ public void testSetGetTimestamp()
+ {
+ long timestamp = System.currentTimeMillis();
+ _testProperties.setTimestamp(timestamp);
+ assertEquals(timestamp, _testProperties.getTimestamp());
+ }
+
+ public void testSetGetType()
+ {
+ String type = "type";
+ _testProperties.setType(type);
+ assertEquals(type, _testProperties.getTypeAsString());
+ }
+
+ public void testSetGetUserId()
+ {
+ String userId = "userId";
+ _testProperties.setUserId(userId);
+ assertEquals(userId, _testProperties.getUserIdAsString());
+ }
+
+ public void testSetGetAppId()
+ {
+ String appId = "appId";
+ _testProperties.setAppId(appId);
+ assertEquals(appId, _testProperties.getAppIdAsString());
+ }
+
+ public void testSetGetClusterId()
+ {
+ String clusterId = "clusterId";
+ _testProperties.setClusterId(clusterId);
+ assertEquals(clusterId, _testProperties.getClusterIdAsString());
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
new file mode 100644
index 0000000000..007da7423e
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
@@ -0,0 +1,960 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQPInvalidClassException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyFieldTableTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(PropertyFieldTableTest.class);
+
+ /**
+ * Test that setting a similar named value replaces any previous value set on that name
+ */
+ public void testReplacement()
+ {
+ FieldTable table1 = new FieldTable();
+ // Set a boolean value
+ table1.setBoolean("value", true);
+ // Check length of table is correct (<Value length> + <type> + <Boolean length>)
+ int size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, table1.getEncodedSize());
+
+ // reset value to an integer
+ table1.setInteger("value", Integer.MAX_VALUE);
+
+ // Check the length has changed accordingly (<Value length> + <type> + <Integer length>)
+ size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, table1.getEncodedSize());
+
+ // Check boolean value is null
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ // ... and integer value is good
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table1.getInteger("value"));
+ }
+
+ /**
+ * Set a boolean and check that we can only get it back as a boolean and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testBoolean()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setBoolean("value", true);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Test Getting right value back
+ Assert.assertEquals((Boolean) true, table1.getBoolean("value"));
+
+ // Check we don't get anything back for other gets
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // except value as a string
+ Assert.assertEquals("true", table1.getString("value"));
+
+ table1.remove("value");
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getBoolean("Rubbish"));
+ }
+
+ /**
+ * Set a byte and check that we can only get it back as a byte and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testByte()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setByte("value", Byte.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Byte.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getByte("Rubbish"));
+ }
+
+ /**
+ * Set a short and check that we can only get it back as a short and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testShort()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setShort("value", Short.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(Short.MAX_VALUE, (short) table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Short.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getShort("Rubbish"));
+ }
+
+ /**
+ * Set a char and check that we can only get it back as a char
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testChar()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setChar("value", 'c');
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals('c', (char) table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("c", table1.getString("value"));
+
+ table1.remove("value");
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getCharacter("Rubbish"));
+ }
+
+ /**
+ * Set a double and check that we can only get it back as a double
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testDouble()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setDouble("value", Double.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(Double.MAX_VALUE, (double) table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Double.MAX_VALUE, table1.getString("value"));
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getDouble("Rubbish"));
+ }
+
+ /**
+ * Set a float and check that we can only get it back as a float
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testFloat()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setFloat("value", Float.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(Float.MAX_VALUE, (float) table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Float.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getFloat("Rubbish"));
+ }
+
+ /**
+ * Set an int and check that we can only get it back as an int
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testInt()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setInteger("value", Integer.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(Integer.MAX_VALUE, (int) table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Integer.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getInteger("Rubbish"));
+ }
+
+ /**
+ * Set a long and check that we can only get it back as a long
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testLong()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setLong("value", Long.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(Long.MAX_VALUE, (long) table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Long.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getLong("Rubbish"));
+ }
+
+ /**
+ * Set a double and check that we can only get it back as a double
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testBytes()
+ {
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+
+ FieldTable table1 = new FieldTable();
+ table1.setBytes("value", bytes);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ assertBytesEqual(bytes, table1.getBytes("value"));
+
+ // ... and a the string value of it is null
+ Assert.assertEquals(null, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getBytes("Rubbish"));
+ }
+
+ /**
+ * Calls all methods that can be used to check the table is empty
+ * - getEncodedSize
+ * - isEmpty
+ * - length
+ *
+ * @param table to check is empty
+ */
+ private void checkEmpty(FieldTable table)
+ {
+ Assert.assertEquals(0, table.getEncodedSize());
+ Assert.assertTrue(table.isEmpty());
+ Assert.assertEquals(0, table.size());
+
+ Assert.assertEquals(0, table.keySet().size());
+ }
+
+ /**
+ * Set a String and check that we can only get it back as a String
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testString()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setString("value", "Hello");
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+ Assert.assertEquals("Hello", table1.getString("value"));
+
+ // Try setting a null value and read it back
+ table1.setString("value", null);
+
+ Assert.assertEquals(null, table1.getString("value"));
+
+ // but still contains the value
+ Assert.assertTrue(table1.containsKey("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getString("Rubbish"));
+
+ // Additional Test that haven't been covered for string
+ table1.setObject("value", "Hello");
+ // Check that it was set correctly
+ Assert.assertEquals("Hello", table1.getString("value"));
+ }
+
+ /** Check that a nested field table parameter correctly encodes and decodes to a byte buffer. */
+ public void testNestedFieldTable()
+ {
+ byte[] testBytes = new byte[] { 0, 1, 2, 3, 4, 5 };
+
+ FieldTable outerTable = new FieldTable();
+ FieldTable innerTable = new FieldTable();
+
+ // Put some stuff in the inner table.
+ innerTable.setBoolean("bool", true);
+ innerTable.setByte("byte", Byte.MAX_VALUE);
+ innerTable.setBytes("bytes", testBytes);
+ innerTable.setChar("char", 'c');
+ innerTable.setDouble("double", Double.MAX_VALUE);
+ innerTable.setFloat("float", Float.MAX_VALUE);
+ innerTable.setInteger("int", Integer.MAX_VALUE);
+ innerTable.setLong("long", Long.MAX_VALUE);
+ innerTable.setShort("short", Short.MAX_VALUE);
+ innerTable.setString("string", "hello");
+ innerTable.setString("null-string", null);
+
+ // Put the inner table in the outer one.
+ outerTable.setFieldTable("innerTable", innerTable);
+
+ // Write the outer table into the buffer.
+ final ByteBuffer buffer = ByteBuffer.allocate((int) outerTable.getEncodedSize() + 4);
+ outerTable.writeToBuffer(buffer);
+ buffer.flip();
+
+ // Extract the table back from the buffer again.
+ try
+ {
+ FieldTable extractedOuterTable = EncodingUtils.readFieldTable(buffer);
+
+ FieldTable extractedTable = extractedOuterTable.getFieldTable("innerTable");
+
+ Assert.assertEquals((Boolean) true, extractedTable.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, extractedTable.getByte("byte"));
+ assertBytesEqual(testBytes, extractedTable.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', extractedTable.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, extractedTable.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, extractedTable.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, extractedTable.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, extractedTable.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, extractedTable.getShort("short"));
+ Assert.assertEquals("hello", extractedTable.getString("string"));
+ Assert.assertEquals(null, extractedTable.getString("null-string"));
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ fail("Failed to decode field table with nested inner table.");
+ }
+ }
+
+ public void testValues()
+ {
+ FieldTable table = new FieldTable();
+ table.setBoolean("bool", true);
+ table.setByte("byte", Byte.MAX_VALUE);
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+ table.setBytes("bytes", bytes);
+ table.setChar("char", 'c');
+ table.setDouble("double", Double.MAX_VALUE);
+ table.setFloat("float", Float.MAX_VALUE);
+ table.setInteger("int", Integer.MAX_VALUE);
+ table.setLong("long", Long.MAX_VALUE);
+ table.setShort("short", Short.MAX_VALUE);
+ table.setString("string", "Hello");
+ table.setString("null-string", null);
+
+ table.setObject("object-bool", true);
+ table.setObject("object-byte", Byte.MAX_VALUE);
+ table.setObject("object-bytes", bytes);
+ table.setObject("object-char", 'c');
+ table.setObject("object-double", Double.MAX_VALUE);
+ table.setObject("object-float", Float.MAX_VALUE);
+ table.setObject("object-int", Integer.MAX_VALUE);
+ table.setObject("object-long", Long.MAX_VALUE);
+ table.setObject("object-short", Short.MAX_VALUE);
+ table.setObject("object-string", "Hello");
+
+ Assert.assertEquals((Boolean) true, table.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, table.getByte("byte"));
+ assertBytesEqual(bytes, table.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', table.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, table.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, table.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, table.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, table.getShort("short"));
+ Assert.assertEquals("Hello", table.getString("string"));
+ Assert.assertEquals(null, table.getString("null-string"));
+
+ Assert.assertEquals(true, table.getObject("object-bool"));
+ Assert.assertEquals(Byte.MAX_VALUE, table.getObject("object-byte"));
+ assertBytesEqual(bytes, (byte[]) table.getObject("object-bytes"));
+ Assert.assertEquals('c', table.getObject("object-char"));
+ Assert.assertEquals(Double.MAX_VALUE, table.getObject("object-double"));
+ Assert.assertEquals(Float.MAX_VALUE, table.getObject("object-float"));
+ Assert.assertEquals(Integer.MAX_VALUE, table.getObject("object-int"));
+ Assert.assertEquals(Long.MAX_VALUE, table.getObject("object-long"));
+ Assert.assertEquals(Short.MAX_VALUE, table.getObject("object-short"));
+ Assert.assertEquals("Hello", table.getObject("object-string"));
+ }
+
+ public void testwriteBuffer()
+ {
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+
+ FieldTable table = new FieldTable();
+ table.setBoolean("bool", true);
+ table.setByte("byte", Byte.MAX_VALUE);
+
+ table.setBytes("bytes", bytes);
+ table.setChar("char", 'c');
+ table.setDouble("double", Double.MAX_VALUE);
+ table.setFloat("float", Float.MAX_VALUE);
+ table.setInteger("int", Integer.MAX_VALUE);
+ table.setLong("long", Long.MAX_VALUE);
+ table.setShort("short", Short.MAX_VALUE);
+ table.setString("string", "hello");
+ table.setString("null-string", null);
+
+ final ByteBuffer buffer = ByteBuffer.allocate((int) table.getEncodedSize() + 4); // FIXME XXX: Is cast a problem?
+
+ table.writeToBuffer(buffer);
+
+ buffer.flip();
+
+ long length = buffer.getUnsignedInt();
+
+ try
+ {
+ FieldTable table2 = new FieldTable(buffer, length);
+
+ Assert.assertEquals((Boolean) true, table2.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, table2.getByte("byte"));
+ assertBytesEqual(bytes, table2.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', table2.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, table2.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, table2.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table2.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, table2.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, table2.getShort("short"));
+ Assert.assertEquals("hello", table2.getString("string"));
+ Assert.assertEquals(null, table2.getString("null-string"));
+
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ e.printStackTrace();
+ fail("PFT should be instantiated from bytes." + e.getCause());
+ }
+ }
+
+ public void testEncodingSize()
+ {
+ FieldTable result = new FieldTable();
+ int size = 0;
+
+ result.setBoolean("boolean", true);
+ size += 1 + EncodingUtils.encodedShortStringLength("boolean") + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setByte("byte", (byte) Byte.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("byte") + EncodingUtils.encodedByteLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ byte[] _bytes = { 99, 98, 97, 96, 95 };
+
+ result.setBytes("bytes", _bytes);
+ size += 1 + EncodingUtils.encodedShortStringLength("bytes") + 4 + _bytes.length;
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setChar("char", (char) 'c');
+ size += 1 + EncodingUtils.encodedShortStringLength("char") + EncodingUtils.encodedCharLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setDouble("double", (double) Double.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("double") + EncodingUtils.encodedDoubleLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setFloat("float", (float) Float.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("float") + EncodingUtils.encodedFloatLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setInteger("int", (int) Integer.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("int") + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setLong("long", (long) Long.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("long") + EncodingUtils.encodedLongLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setShort("short", (short) Short.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("short") + EncodingUtils.encodedShortLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setString("result", "Hello");
+ size += 1 + EncodingUtils.encodedShortStringLength("result") + EncodingUtils.encodedLongStringLength("Hello");
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-bool", true);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-bool") + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-byte", Byte.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-byte") + EncodingUtils.encodedByteLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-bytes", _bytes);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-bytes") + 4 + _bytes.length;
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-char", 'c');
+ size += 1 + EncodingUtils.encodedShortStringLength("object-char") + EncodingUtils.encodedCharLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-double", Double.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-double") + EncodingUtils.encodedDoubleLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-float", Float.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-float") + EncodingUtils.encodedFloatLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-int", Integer.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-int") + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-long", Long.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-long") + EncodingUtils.encodedLongLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-short", Short.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-short") + EncodingUtils.encodedShortLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ }
+
+ // public void testEncodingSize1()
+ // {
+ // PropertyFieldTable table = new PropertyFieldTable();
+ // int length = 0;
+ // result.put("one", 1L);
+ // length = EncodingUtils.encodedShortStringLength("one");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("two", 2L);
+ // length += EncodingUtils.encodedShortStringLength("two");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("three", 3L);
+ // length += EncodingUtils.encodedShortStringLength("three");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("four", 4L);
+ // length += EncodingUtils.encodedShortStringLength("four");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("five", 5L);
+ // length += EncodingUtils.encodedShortStringLength("five");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // //fixme should perhaps be expanded to incorporate all types.
+ //
+ // final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem?
+ //
+ // result.writeToBuffer(buffer);
+ //
+ // buffer.flip();
+ //
+ // long length = buffer.getUnsignedInt();
+ //
+ // try
+ // {
+ // PropertyFieldTable table2 = new PropertyFieldTable(buffer, length);
+ //
+ // Assert.assertEquals((Long) 1L, table2.getLong("one"));
+ // Assert.assertEquals((Long) 2L, table2.getLong("two"));
+ // Assert.assertEquals((Long) 3L, table2.getLong("three"));
+ // Assert.assertEquals((Long) 4L, table2.getLong("four"));
+ // Assert.assertEquals((Long) 5L, table2.getLong("five"));
+ // }
+ // catch (AMQFrameDecodingException e)
+ // {
+ // e.printStackTrace();
+ // fail("PFT should be instantiated from bytes." + e.getCause());
+ // }
+ //
+ // }
+
+ /**
+ * Additional test for setObject
+ */
+ public void testSetObject()
+ {
+ FieldTable table = new FieldTable();
+
+ // Try setting a non primative object
+
+ try
+ {
+ table.setObject("value", this);
+ fail("Only primative values allowed in setObject");
+ }
+ catch (AMQPInvalidClassException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept Null
+ */
+ public void testCheckPropertyNameasNull()
+ {
+ FieldTable table = new FieldTable();
+
+ try
+ {
+ table.setObject((String) null, "String");
+ fail("Null property name is not allowed");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept an empty String
+ */
+ public void testCheckPropertyNameasEmptyString()
+ {
+ FieldTable table = new FieldTable();
+
+ try
+ {
+ table.setObject("", "String");
+ fail("empty property name is not allowed");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept an empty String
+ */
+ public void testCheckPropertyNamehasMaxLength()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ StringBuffer longPropertyName = new StringBuffer(129);
+
+ for (int i = 0; i < 129; i++)
+ {
+ longPropertyName.append("x");
+ }
+
+ try
+ {
+ table.setObject(longPropertyName.toString(), "String");
+ fail("property name must be < 128 characters");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test checkPropertyName starts with a letter
+ */
+ public void testCheckPropertyNameStartCharacterIsLetter()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ // Try a name that starts with a number
+ try
+ {
+ table.setObject("1", "String");
+ fail("property name must start with a letter");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test checkPropertyName starts with a hash or a dollar
+ */
+ public void testCheckPropertyNameStartCharacterIsHashorDollar()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ // Try a name that starts with a number
+ try
+ {
+ table.setObject("#", "String");
+ table.setObject("$", "String");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ fail("property name are allowed to start with # and $s");
+ }
+
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test to test the contents of the table
+ */
+ public void testContents()
+ {
+ FieldTable table = new FieldTable();
+
+ table.setObject("StringProperty", "String");
+
+ Assert.assertEquals("String", table.getString("StringProperty"));
+
+ // Test Clear
+
+ table.clear();
+
+ checkEmpty(table);
+ }
+
+ /**
+ * Test the contents of the sets
+ */
+ public void testSets()
+ {
+
+ FieldTable table = new FieldTable();
+
+ table.setObject("n1", "1");
+ table.setObject("n2", "2");
+ table.setObject("n3", "3");
+
+ Assert.assertEquals("1", table.getObject("n1"));
+ Assert.assertEquals("2", table.getObject("n2"));
+ Assert.assertEquals("3", table.getObject("n3"));
+
+ }
+
+ private void assertBytesEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertEquals(expected[index], actual[index]);
+ }
+ }
+
+ private void assertBytesNotEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertFalse(expected[index] == actual[index]);
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(PropertyFieldTableTest.class);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java
new file mode 100644
index 0000000000..6383d52298
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.pool;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.session.TestSession;
+import org.apache.mina.common.IoFilter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IdleStatus;
+
+import java.util.concurrent.RejectedExecutionException;
+
+public class PoolingFilterTest extends TestCase
+{
+ private PoolingFilter _pool;
+ ReferenceCountingExecutorService _executorService;
+
+ public void setUp()
+ {
+
+ //Create Pool
+ _executorService = ReferenceCountingExecutorService.getInstance();
+ _executorService.acquireExecutorService();
+ _pool = PoolingFilter.createAynschWritePoolingFilter(_executorService,
+ "AsynchronousWriteFilter");
+
+ }
+
+ public void testRejectedExecution() throws Exception
+ {
+
+ TestSession testSession = new TestSession();
+ _pool.createNewJobForSession(testSession);
+ _pool.filterWrite(new NoOpFilter(), testSession, new IoFilter.WriteRequest("Message"));
+
+ //Shutdown the pool
+ _executorService.getPool().shutdownNow();
+
+ try
+ {
+
+ testSession = new TestSession();
+ _pool.createNewJobForSession(testSession);
+ //prior to fix for QPID-172 this would throw RejectedExecutionException
+ _pool.filterWrite(null, testSession, null);
+ }
+ catch (RejectedExecutionException rje)
+ {
+ Assert.fail("RejectedExecutionException should not occur after pool has shutdown:" + rje);
+ }
+ }
+
+ private static class NoOpFilter implements IoFilter.NextFilter
+ {
+
+ public void sessionOpened(IoSession session)
+ {
+ }
+
+ public void sessionClosed(IoSession session)
+ {
+ }
+
+ public void sessionIdle(IoSession session, IdleStatus status)
+ {
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause)
+ {
+ }
+
+ public void messageReceived(IoSession session, Object message)
+ {
+ }
+
+ public void messageSent(IoSession session, Object message)
+ {
+ }
+
+ public void filterWrite(IoSession session, IoFilter.WriteRequest writeRequest)
+ {
+ }
+
+ public void filterClose(IoSession session)
+ {
+ }
+
+ public void sessionCreated(IoSession session)
+ {
+ }
+ }
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/session/TestSession.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/session/TestSession.java
new file mode 100644
index 0000000000..aafc91b03b
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/session/TestSession.java
@@ -0,0 +1,277 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.session;
+
+import org.apache.mina.common.*;
+
+import java.net.SocketAddress;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class TestSession implements IoSession
+{
+ private final ConcurrentMap attributes = new ConcurrentHashMap();
+
+ public TestSession()
+ {
+ }
+
+ public IoService getService()
+ {
+ return null; //TODO
+ }
+
+ public IoServiceConfig getServiceConfig()
+ {
+ return null; //TODO
+ }
+
+ public IoHandler getHandler()
+ {
+ return null; //TODO
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return null; //TODO
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return null; //TODO
+ }
+
+ public WriteFuture write(Object message)
+ {
+ return null; //TODO
+ }
+
+ public CloseFuture close()
+ {
+ return null; //TODO
+ }
+
+ public Object getAttachment()
+ {
+ return getAttribute("");
+ }
+
+ public Object setAttachment(Object attachment)
+ {
+ return setAttribute("",attachment);
+ }
+
+ public Object getAttribute(String key)
+ {
+ return attributes.get(key);
+ }
+
+ public Object setAttribute(String key, Object value)
+ {
+ return attributes.put(key,value);
+ }
+
+ public Object setAttribute(String key)
+ {
+ return attributes.put(key, Boolean.TRUE);
+ }
+
+ public Object removeAttribute(String key)
+ {
+ return attributes.remove(key);
+ }
+
+ public boolean containsAttribute(String key)
+ {
+ return attributes.containsKey(key);
+ }
+
+ public Set getAttributeKeys()
+ {
+ return attributes.keySet();
+ }
+
+ public TransportType getTransportType()
+ {
+ return null; //TODO
+ }
+
+ public boolean isConnected()
+ {
+ return false; //TODO
+ }
+
+ public boolean isClosing()
+ {
+ return false; //TODO
+ }
+
+ public CloseFuture getCloseFuture()
+ {
+ return null; //TODO
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ return null; //TODO
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ return null; //TODO
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return null; //TODO
+ }
+
+ public int getIdleTime(IdleStatus status)
+ {
+ return 0; //TODO
+ }
+
+ public long getIdleTimeInMillis(IdleStatus status)
+ {
+ return 0; //TODO
+ }
+
+ public void setIdleTime(IdleStatus status, int idleTime)
+ {
+ //TODO
+ }
+
+ public int getWriteTimeout()
+ {
+ return 0; //TODO
+ }
+
+ public long getWriteTimeoutInMillis()
+ {
+ return 0; //TODO
+ }
+
+ public void setWriteTimeout(int writeTimeout)
+ {
+ //TODO
+ }
+
+ public TrafficMask getTrafficMask()
+ {
+ return null; //TODO
+ }
+
+ public void setTrafficMask(TrafficMask trafficMask)
+ {
+ //TODO
+ }
+
+ public void suspendRead()
+ {
+ //TODO
+ }
+
+ public void suspendWrite()
+ {
+ //TODO
+ }
+
+ public void resumeRead()
+ {
+ //TODO
+ }
+
+ public void resumeWrite()
+ {
+ //TODO
+ }
+
+ public long getReadBytes()
+ {
+ return 0; //TODO
+ }
+
+ public long getWrittenBytes()
+ {
+ return 0; //TODO
+ }
+
+ public long getReadMessages()
+ {
+ return 0;
+ }
+
+ public long getWrittenMessages()
+ {
+ return 0;
+ }
+
+ public long getWrittenWriteRequests()
+ {
+ return 0; //TODO
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ return 0; //TODO
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ return 0; //TODO
+ }
+
+ public long getCreationTime()
+ {
+ return 0; //TODO
+ }
+
+ public long getLastIoTime()
+ {
+ return 0; //TODO
+ }
+
+ public long getLastReadTime()
+ {
+ return 0; //TODO
+ }
+
+ public long getLastWriteTime()
+ {
+ return 0; //TODO
+ }
+
+ public boolean isIdle(IdleStatus status)
+ {
+ return false; //TODO
+ }
+
+ public int getIdleCount(IdleStatus status)
+ {
+ return 0; //TODO
+ }
+
+ public long getLastIdleTime(IdleStatus status)
+ {
+ return 0; //TODO
+ }
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java
new file mode 100644
index 0000000000..7ce41db4d0
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java
@@ -0,0 +1,385 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import org.apache.mina.util.AvailablePortFinder;
+
+import org.apache.qpid.util.concurrent.Condition;
+
+import org.apache.qpid.transport.network.ConnectionBinding;
+import org.apache.qpid.transport.network.io.IoAcceptor;
+import org.apache.qpid.transport.util.Logger;
+import org.apache.qpid.transport.util.Waiter;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import java.io.IOException;
+
+import static org.apache.qpid.transport.Option.*;
+
+/**
+ * ConnectionTest
+ */
+
+public class ConnectionTest extends TestCase implements SessionListener
+{
+
+ private static final Logger log = Logger.get(ConnectionTest.class);
+
+ private int port;
+ private volatile boolean queue = false;
+ private List<MessageTransfer> messages = new ArrayList<MessageTransfer>();
+ private List<MessageTransfer> incoming = new ArrayList<MessageTransfer>();
+
+ private IoAcceptor _ioa = null;
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ port = AvailablePortFinder.getNextAvailable(12000);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ if (_ioa != null)
+ {
+ _ioa.close();
+ }
+
+ super.tearDown();
+ }
+
+ public void opened(Session ssn) {}
+
+ public void message(final Session ssn, MessageTransfer xfr)
+ {
+ if (queue)
+ {
+ messages.add(xfr);
+ ssn.processed(xfr);
+ return;
+ }
+
+ String body = xfr.getBodyString();
+
+ if (body.startsWith("CLOSE"))
+ {
+ ssn.getConnection().close();
+ }
+ else if (body.startsWith("DELAYED_CLOSE"))
+ {
+ ssn.processed(xfr);
+ new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ ssn.getConnection().close();
+ }
+ }.start();
+ }
+ else if (body.startsWith("ECHO"))
+ {
+ int id = xfr.getId();
+ ssn.invoke(xfr);
+ ssn.processed(id);
+ }
+ else if (body.startsWith("SINK"))
+ {
+ ssn.processed(xfr);
+ }
+ else if (body.startsWith("DROP"))
+ {
+ // do nothing
+ }
+ else
+ {
+ throw new IllegalArgumentException
+ ("unrecognized message: " + body);
+ }
+ }
+
+ public void exception(Session ssn, SessionException exc)
+ {
+ throw exc;
+ }
+
+ public void closed(Session ssn) {}
+
+ private void send(Session ssn, String msg)
+ {
+ ssn.messageTransfer
+ ("xxx", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED,
+ null, msg);
+ }
+
+ private Connection connect(final Condition closed)
+ {
+ Connection conn = new Connection();
+ conn.setConnectionListener(new ConnectionListener()
+ {
+ public void opened(Connection conn) {}
+ public void exception(Connection conn, ConnectionException exc)
+ {
+ exc.printStackTrace();
+ }
+ public void closed(Connection conn)
+ {
+ if (closed != null)
+ {
+ closed.set();
+ }
+ }
+ });
+ conn.connect("localhost", port, null, "guest", "guest", false);
+ return conn;
+ }
+
+ public void testProtocolNegotiationExceptionOverridesCloseException() throws Exception
+ {
+ // Force os.name to be windows to exercise code in IoReceiver
+ // that looks for the value of os.name
+ System.setProperty("os.name","windows");
+
+ // Start server as 0-9 to froce a ProtocolVersionException
+ startServer(new ProtocolHeader(1, 0, 9));
+
+ Condition closed = new Condition();
+
+ try
+ {
+ connect(closed);
+ fail("ProtocolVersionException expected");
+ }
+ catch (ProtocolVersionException pve)
+ {
+ //Expected code path
+ }
+ catch (Exception e)
+ {
+ fail("ProtocolVersionException expected. Got:" + e.getMessage());
+ }
+ }
+
+ private void startServer()
+ {
+ startServer(new ProtocolHeader(1, 0, 10));
+ }
+
+ private void startServer(final ProtocolHeader protocolHeader)
+ {
+ ConnectionDelegate server = new ServerDelegate()
+ {
+ @Override
+ public void init(Connection conn, ProtocolHeader hdr)
+ {
+ conn.send(protocolHeader);
+ List<Object> utf8 = new ArrayList<Object>();
+ utf8.add("utf8");
+ conn.connectionStart(null, Collections.EMPTY_LIST, utf8);
+ }
+
+ @Override
+ public Session getSession(Connection conn, SessionAttach atc)
+ {
+ Session ssn = super.getSession(conn, atc);
+ ssn.setSessionListener(ConnectionTest.this);
+ return ssn;
+ }
+ };
+
+ try
+ {
+ _ioa = new IoAcceptor("localhost", port, ConnectionBinding.get(server));
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ fail("Unable to start Server for test due to:" + e.getMessage());
+ }
+
+ _ioa.start();
+ }
+
+ public void testClosedNotificationAndWriteToClosed() throws Exception
+ {
+ startServer();
+
+ Condition closed = new Condition();
+ Connection conn = connect(closed);
+
+ Session ssn = conn.createSession(1);
+ send(ssn, "CLOSE");
+
+ if (!closed.get(3000))
+ {
+ fail("never got notified of connection close");
+ }
+
+ try
+ {
+ conn.connectionCloseOk();
+ fail("writing to a closed socket succeeded");
+ }
+ catch (TransportException e)
+ {
+ // expected
+ }
+ }
+
+ class FailoverConnectionListener implements ConnectionListener
+ {
+ public void opened(Connection conn) {}
+
+ public void exception(Connection conn, ConnectionException e)
+ {
+ throw e;
+ }
+
+ public void closed(Connection conn)
+ {
+ queue = true;
+ conn.connect("localhost", port, null, "guest", "guest");
+ conn.resume();
+ }
+ }
+
+ class TestSessionListener implements SessionListener
+ {
+ public void opened(Session s) {}
+ public void exception(Session s, SessionException e) {}
+ public void message(Session s, MessageTransfer xfr)
+ {
+ synchronized (incoming)
+ {
+ incoming.add(xfr);
+ incoming.notifyAll();
+ }
+
+ s.processed(xfr);
+ }
+ public void closed(Session s) {}
+ }
+
+ public void testResumeNonemptyReplayBuffer() throws Exception
+ {
+ startServer();
+
+ Connection conn = new Connection();
+ conn.setConnectionListener(new FailoverConnectionListener());
+ conn.connect("localhost", port, null, "guest", "guest");
+ Session ssn = conn.createSession(1);
+ ssn.setSessionListener(new TestSessionListener());
+
+ send(ssn, "SINK 0");
+ send(ssn, "ECHO 1");
+ send(ssn, "ECHO 2");
+
+ ssn.sync();
+
+ String[] msgs = { "DROP 3", "DROP 4", "DROP 5", "CLOSE 6", "SINK 7" };
+ for (String m : msgs)
+ {
+ send(ssn, m);
+ }
+
+ ssn.sync();
+
+ assertEquals(msgs.length, messages.size());
+ for (int i = 0; i < msgs.length; i++)
+ {
+ assertEquals(msgs[i], messages.get(i).getBodyString());
+ }
+
+ queue = false;
+
+ send(ssn, "ECHO 8");
+ send(ssn, "ECHO 9");
+
+ synchronized (incoming)
+ {
+ Waiter w = new Waiter(incoming, 30000);
+ while (w.hasTime() && incoming.size() < 4)
+ {
+ w.await();
+ }
+
+ assertEquals(4, incoming.size());
+ assertEquals("ECHO 1", incoming.get(0).getBodyString());
+ assertEquals(0, incoming.get(0).getId());
+ assertEquals("ECHO 2", incoming.get(1).getBodyString());
+ assertEquals(1, incoming.get(1).getId());
+ assertEquals("ECHO 8", incoming.get(2).getBodyString());
+ assertEquals(0, incoming.get(0).getId());
+ assertEquals("ECHO 9", incoming.get(3).getBodyString());
+ assertEquals(1, incoming.get(1).getId());
+ }
+ }
+
+ public void testResumeEmptyReplayBuffer() throws InterruptedException
+ {
+ startServer();
+
+ Connection conn = new Connection();
+ conn.setConnectionListener(new FailoverConnectionListener());
+ conn.connect("localhost", port, null, "guest", "guest");
+ Session ssn = conn.createSession(1);
+ ssn.setSessionListener(new TestSessionListener());
+
+ send(ssn, "SINK 0");
+ send(ssn, "SINK 1");
+ send(ssn, "DELAYED_CLOSE 2");
+ ssn.sync();
+ Thread.sleep(6000);
+ send(ssn, "SINK 3");
+ ssn.sync();
+ System.out.println(messages);
+ assertEquals(1, messages.size());
+ assertEquals("SINK 3", messages.get(0).getBodyString());
+ }
+
+ public void testFlushExpected() throws InterruptedException
+ {
+ startServer();
+
+ Connection conn = new Connection();
+ conn.connect("localhost", port, null, "guest", "guest");
+ Session ssn = conn.createSession();
+ ssn.sessionFlush(EXPECTED);
+ send(ssn, "SINK 0");
+ ssn.sessionFlush(EXPECTED);
+ send(ssn, "SINK 1");
+ ssn.sync();
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/GenTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/GenTest.java
new file mode 100644
index 0000000000..512a0a29a6
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/GenTest.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.transport;
+
+import junit.framework.TestCase;
+
+/**
+ * GenTest
+ *
+ */
+
+public class GenTest extends TestCase
+{
+
+ public void testBooleans()
+ {
+ QueueDeclare qd = new QueueDeclare().queue("test-queue").durable(false);
+ assertEquals(qd.getQueue(), "test-queue");
+ assertFalse("durable should be false", qd.getDurable());
+ qd.setDurable(true);
+ assertTrue("durable should be true", qd.getDurable());
+ qd.setDurable(false);
+ assertFalse("durable should be false again", qd.getDurable());
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java
new file mode 100644
index 0000000000..ad45d00e46
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java
@@ -0,0 +1,238 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transport;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import static org.apache.qpid.util.Serial.*;
+
+/**
+ * RangeSetTest
+ *
+ */
+
+public class RangeSetTest extends TestCase
+{
+
+ private void check(RangeSet ranges)
+ {
+ List<Integer> posts = new ArrayList<Integer>();
+ for (Range range : ranges)
+ {
+ posts.add(range.getLower());
+ posts.add(range.getUpper());
+ }
+
+ List<Integer> sorted = new ArrayList<Integer>(posts);
+ Collections.sort(sorted, COMPARATOR);
+
+ assertEquals(posts, sorted);
+
+ int idx = 1;
+ while (idx + 1 < posts.size())
+ {
+ assertTrue(!eq(posts.get(idx) + 1, posts.get(idx+1)));
+ idx += 2;
+ }
+ }
+
+ public void test1()
+ {
+ RangeSet ranges = new RangeSet();
+ ranges.add(5, 10);
+ check(ranges);
+ ranges.add(15, 20);
+ check(ranges);
+ ranges.add(23, 25);
+ check(ranges);
+ ranges.add(12, 14);
+ check(ranges);
+ ranges.add(0, 1);
+ check(ranges);
+ ranges.add(3, 11);
+ check(ranges);
+ }
+
+ public void test2()
+ {
+ RangeSet rs = new RangeSet();
+ check(rs);
+
+ rs.add(1);
+ assertTrue(rs.includes(1));
+ assertTrue(!rs.includes(2));
+ assertTrue(!rs.includes(0));
+ check(rs);
+
+ rs.add(2);
+ assertTrue(!rs.includes(0));
+ assertTrue(rs.includes(1));
+ assertTrue(rs.includes(2));
+ assertTrue(!rs.includes(3));
+ check(rs);
+
+ rs.add(0);
+
+ assertTrue(!rs.includes(-1));
+ assertTrue(rs.includes(0));
+ assertTrue(rs.includes(1));
+ assertTrue(rs.includes(2));
+ assertTrue(!rs.includes(3));
+ check(rs);
+
+ rs.add(37);
+
+ assertTrue(!rs.includes(-1));
+ assertTrue(rs.includes(0));
+ assertTrue(rs.includes(1));
+ assertTrue(rs.includes(2));
+ assertTrue(!rs.includes(3));
+ assertTrue(!rs.includes(36));
+ assertTrue(rs.includes(37));
+ assertTrue(!rs.includes(38));
+ check(rs);
+
+ rs.add(-1);
+ check(rs);
+
+ rs.add(-3);
+ check(rs);
+
+ rs.add(1, 20);
+ assertTrue(!rs.includes(21));
+ assertTrue(rs.includes(20));
+ check(rs);
+ }
+
+ public void testAddSelf()
+ {
+ RangeSet a = new RangeSet();
+ a.add(0, 8);
+ check(a);
+ a.add(0, 8);
+ check(a);
+ assertEquals(a.size(), 1);
+ Range range = a.iterator().next();
+ assertEquals(range.getLower(), 0);
+ assertEquals(range.getUpper(), 8);
+ }
+
+ public void testIntersect1()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(9, 20);
+ Range i1 = a.intersect(b);
+ Range i2 = b.intersect(a);
+ assertEquals(i1.getUpper(), 10);
+ assertEquals(i2.getUpper(), 10);
+ assertEquals(i1.getLower(), 9);
+ assertEquals(i2.getLower(), 9);
+ }
+
+ public void testIntersect2()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(11, 20);
+ assertNull(a.intersect(b));
+ assertNull(b.intersect(a));
+ }
+
+ public void testIntersect3()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(3, 5);
+ Range i1 = a.intersect(b);
+ Range i2 = b.intersect(a);
+ assertEquals(i1.getUpper(), 5);
+ assertEquals(i2.getUpper(), 5);
+ assertEquals(i1.getLower(), 3);
+ assertEquals(i2.getLower(), 3);
+ }
+
+ public void testSubtract1()
+ {
+ Range a = new Range(0, 10);
+ assertTrue(a.subtract(a).isEmpty());
+ }
+
+ public void testSubtract2()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(20, 30);
+ List<Range> ranges = a.subtract(b);
+ assertEquals(ranges.size(), 1);
+ Range d = ranges.get(0);
+ assertEquals(d.getLower(), a.getLower());
+ assertEquals(d.getUpper(), a.getUpper());
+ }
+
+ public void testSubtract3()
+ {
+ Range a = new Range(20, 30);
+ Range b = new Range(0, 10);
+ List<Range> ranges = a.subtract(b);
+ assertEquals(ranges.size(), 1);
+ Range d = ranges.get(0);
+ assertEquals(d.getLower(), a.getLower());
+ assertEquals(d.getUpper(), a.getUpper());
+ }
+
+ public void testSubtract4()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(3, 5);
+ List<Range> ranges = a.subtract(b);
+ assertEquals(ranges.size(), 2);
+ Range low = ranges.get(0);
+ Range high = ranges.get(1);
+ assertEquals(low.getLower(), 0);
+ assertEquals(low.getUpper(), 2);
+ assertEquals(high.getLower(), 6);
+ assertEquals(high.getUpper(), 10);
+ }
+
+ public void testSubtract5()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(3, 20);
+ List<Range> ranges = a.subtract(b);
+ assertEquals(ranges.size(), 1);
+ Range d = ranges.get(0);
+ assertEquals(d.getLower(), 0);
+ assertEquals(d.getUpper(), 2);
+ }
+
+ public void testSubtract6()
+ {
+ Range a = new Range(0, 10);
+ Range b = new Range(-10, 5);
+ List<Range> ranges = a.subtract(b);
+ assertEquals(ranges.size(), 1);
+ Range d = ranges.get(0);
+ assertEquals(d.getLower(), 6);
+ assertEquals(d.getUpper(), 10);
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java
new file mode 100644
index 0000000000..942901f1c0
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java
@@ -0,0 +1,554 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.util;
+
+import junit.framework.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+/**
+ * Unit tests the {@link CommandLineParser} class.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that parsing a single flag works ok.
+ * <tr><td> Check that parsing multiple flags condensed together works ok.
+ * <tr><td> Check that parsing an option with a space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with no space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with specific argument format works ok.
+ * <tr><td> Check that parsing an option with specific argument format fails on bad argument.
+ * <tr><td> Check that parsing a flag condensed together with an option fails.
+ * <tr><td> Check that parsing a free argument works ok.
+ * <tr><td> Check that parsing a free argument with specific format works ok.
+ * <tr><td> Check that parsing a free argument with specific format fails on bad argument.
+ * <tr><td> Check that parsing a mandatory option works ok.
+ * <tr><td> Check that parsing a mandatory free argument works ok.
+ * <tr><td> Check that parsing a mandatory option fails when no option is set.
+ * <tr><td> Check that parsing a mandatory free argument fails when no argument is specified.
+ * <tr><td> Check that parsing an unknown option works when unknowns not errors.
+ * <tr><td> Check that parsing an unknown flag fails when unknowns are to be reported as errors.
+ * <tr><td> Check that parsing an unknown option fails when unknowns are to be reported as errors.
+ * <tr><td> Check that get errors returns a string on errors.
+ * <tr><td> Check that get errors returns an empty string on no errors.
+ * <tr><td> Check that get usage returns a string.
+ * <tr><td> Check that get options in force returns an empty string before parsing.
+ * <tr><td> Check that get options in force return a non-empty string after parsing.
+ * </table>
+ */
+public class CommandLineParserTest extends TestCase
+{
+ private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class);
+
+ public CommandLineParserTest(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Compile all the tests for the default test implementation of a traversable state into a test suite.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("CommandLineParser Tests");
+
+ // Add all the tests defined in this class (using the default constructor)
+ suite.addTestSuite(CommandLineParserTest.class);
+
+ return suite;
+ }
+
+ /** Check that get errors returns an empty string on no errors. */
+ public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some legal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the get errors message returns an empty string.
+ assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors()));
+ }
+
+ /** Check that get errors returns a string on errors. */
+ public void testGetErrorsReturnsStringOnErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ try
+ {
+ // Do some illegal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" });
+ }
+ catch (IllegalArgumentException e)
+ { }
+
+ // Check that the get errors message returns a string.
+ assertTrue("The errors method returned an empty string.",
+ !((parser.getErrors() == null) || "".equals(parser.getErrors())));
+
+ }
+
+ /** Check that get options in force returns an empty string before parsing. */
+ public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the options in force method returns an empty string.
+ assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce()));
+ }
+
+ /** Check that get options in force return a non-empty string after parsing. */
+ public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the options in force method returns a string.
+ assertTrue("The options in force method did not return a non empty string.",
+ !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce())));
+ }
+
+ /** Check that get usage returns a string. */
+ public void testGetUsageReturnsString() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the usage method returns a string.
+ assertTrue("The usage method did not return a non empty string.",
+ !((parser.getUsage() == null) || "".equals(parser.getUsage())));
+ }
+
+ /** Check that parsing multiple flags condensed together works ok. */
+ public void testParseCondensedFlagsOk() throws Exception
+ {
+ // Create a command line parser for multiple flags.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Flag 2." },
+ { "t3", "Test Flag 3." }
+ });
+
+ // Parse a command line with the flags set and condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" });
+
+ // Check that the flags were set in the parsed properties.
+ assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1")));
+ assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2")));
+ assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3")));
+ }
+
+ /** Check that parsing a flag condensed together with an option fails. */
+ public void testParseFlagCondensedWithOptionFails() throws Exception
+ {
+ // Create a command line parser for a flag and an option.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" }
+ });
+
+ // Check that the parser reports an error.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with the flag and option condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format fails on bad argument. */
+ public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format works ok. */
+ public void testParseFormattedFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this argument set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing an option with specific argument format fails on bad argument. */
+ public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing an option with specific argument format works ok. */
+ public void testParseFormattedOptionArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a free argument works ok. */
+ public void testParseFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory option works ok. */
+ public void testParseMandatoryOptionOk() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a mandatory free argument works ok. */
+ public void testParseMandatoryFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory free argument fails when no argument is specified. */
+ public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory free argument is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this free argument not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing a mandatory option fails when no option is set. */
+ public void testParseMandatoryFailsNoOption() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory option is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing an option with no space between it and its argument works ok. */
+ public void testParseOptionWithNoSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with no space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-ttest" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an option with a space between it and its argument works ok. */
+ public void testParseOptionWithSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with a space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a single flag works ok. */
+ public void testParseSingleFlagOk() throws Exception
+ {
+ // Create a command line parser for a single flag.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Flag." }
+ });
+
+ // Parse a command line with the single flag set.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t" });
+
+ // Check that the flag is set in the parsed properties.
+ assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t")));
+
+ // Reset the parser.
+ parser.reset();
+
+ // Parse a command line with the single flag not set.
+ testProps = parser.parseCommandLine(new String[] {});
+
+ // Check that the flag is cleared in the parsed properties.
+ assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an unknown option works when unknowns not errors. */
+ public void testParseUnknownOptionOk() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Check that parsing does not fail on an unknown flag.
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off.");
+ }
+ }
+
+ /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */
+ public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.",
+ testPassed);
+ }
+
+ /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */
+ public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t", "test" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.",
+ testPassed);
+ }
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java
new file mode 100644
index 0000000000..fb367d042c
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java
@@ -0,0 +1,522 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.FileNotFoundException;
+
+public class FileUtilsTest extends TestCase
+{
+ private static final String COPY = "-Copy";
+ private static final String SUB = "-Sub";
+
+ /**
+ * Additional test for the copy method.
+ * Ensures that the directory count did increase by more than 1 after the copy.
+ */
+ public void testCopyFile()
+ {
+ final String TEST_DATA = "FileUtilsTest-testCopy-TestDataTestDataTestDataTestDataTestDataTestData";
+ String fileName = "FileUtilsTest-testCopy";
+ String fileNameCopy = fileName + COPY;
+
+ File[] beforeCopyFileList = null;
+
+ //Create initial file
+ File test = createTestFile(fileName, TEST_DATA);
+
+ try
+ {
+ //Check number of files before copy
+ beforeCopyFileList = test.getAbsoluteFile().getParentFile().listFiles();
+ int beforeCopy = beforeCopyFileList.length;
+
+ //Perform Copy
+ File destination = new File(fileNameCopy);
+ FileUtils.copy(test, destination);
+ //Ensure the JVM cleans up if cleanup failues
+ destination.deleteOnExit();
+
+ //Retrieve counts after copy
+ int afterCopy = test.getAbsoluteFile().getParentFile().listFiles().length;
+
+ int afterCopyFromCopy = new File(fileNameCopy).getAbsoluteFile().getParentFile().listFiles().length;
+
+ // Validate the copy counts
+ assertEquals("The file listing from the original and the copy differ in length.", afterCopy, afterCopyFromCopy);
+ assertEquals("The number of files did not increase.", beforeCopy + 1, afterCopy);
+ assertEquals("The number of files did not increase.", beforeCopy + 1, afterCopyFromCopy);
+
+ //Validate copy
+ // Load content
+ String copiedFileContent = FileUtils.readFileAsString(fileNameCopy);
+ assertEquals(TEST_DATA, copiedFileContent);
+ }
+ finally // Ensure clean
+ {
+ //Clean up
+ assertTrue("Unable to cleanup", FileUtils.deleteFile(fileNameCopy));
+
+ //Check file list after cleanup
+ File[] afterCleanup = new File(test.getAbsoluteFile().getParent()).listFiles();
+ checkFileLists(beforeCopyFileList, afterCleanup);
+
+ //Remove original file
+ assertTrue("Unable to cleanup", test.delete());
+ }
+ }
+
+ /**
+ * Create and Copy the following structure:
+ *
+ * testDirectory --+
+ * +-- testSubDirectory --+
+ * +-- testSubFile
+ * +-- File
+ *
+ * to testDirectory-Copy
+ *
+ * Validate that the file count in the copy is correct and contents of the copied files is correct.
+ */
+ public void testCopyRecursive()
+ {
+ final String TEST_DATA = "FileUtilsTest-testDirectoryCopy-TestDataTestDataTestDataTestDataTestDataTestData";
+ String fileName = "FileUtilsTest-testCopy";
+ String TEST_DIR = "testDirectoryCopy";
+
+ //Create Initial Structure
+ File testDir = new File(TEST_DIR);
+
+ //Check number of files before copy
+ File[] beforeCopyFileList = testDir.getAbsoluteFile().getParentFile().listFiles();
+
+ try
+ {
+ //Create Directories
+ assertTrue("Test directory already exists cannot test.", !testDir.exists());
+
+ if (!testDir.mkdir())
+ {
+ fail("Unable to make test Directory");
+ }
+
+ File testSubDir = new File(TEST_DIR + File.separator + TEST_DIR + SUB);
+ if (!testSubDir.mkdir())
+ {
+ fail("Unable to make test sub Directory");
+ }
+
+ //Create Files
+ createTestFile(testDir.toString() + File.separator + fileName, TEST_DATA);
+ createTestFile(testSubDir.toString() + File.separator + fileName + SUB, TEST_DATA);
+
+ //Ensure the JVM cleans up if cleanup failues
+ testSubDir.deleteOnExit();
+ testDir.deleteOnExit();
+
+
+ //Perform Copy
+ File copyDir = new File(testDir.toString() + COPY);
+ try
+ {
+ FileUtils.copyRecursive(testDir, copyDir);
+ }
+ catch (FileNotFoundException e)
+ {
+ fail(e.getMessage());
+ }
+ catch (FileUtils.UnableToCopyException e)
+ {
+ fail(e.getMessage());
+ }
+
+ //Validate Copy
+ assertEquals("Copied directory should only have one file and one directory in it.", 2, copyDir.listFiles().length);
+
+ //Validate Copy File Contents
+ String copiedFileContent = FileUtils.readFileAsString(copyDir.toString() + File.separator + fileName);
+ assertEquals(TEST_DATA, copiedFileContent);
+
+ //Validate Name of Sub Directory
+ assertTrue("Expected subdirectory is not a directory", new File(copyDir.toString() + File.separator + TEST_DIR + SUB).isDirectory());
+
+ //Assert that it contains only one item
+ assertEquals("Copied sub directory should only have one directory in it.", 1, new File(copyDir.toString() + File.separator + TEST_DIR + SUB).listFiles().length);
+
+ //Validate content of Sub file
+ copiedFileContent = FileUtils.readFileAsString(copyDir.toString() + File.separator + TEST_DIR + SUB + File.separator + fileName + SUB);
+ assertEquals(TEST_DATA, copiedFileContent);
+ }
+ finally
+ {
+ //Clean up source and copy directory.
+ assertTrue("Unable to cleanup", FileUtils.delete(testDir, true));
+ assertTrue("Unable to cleanup", FileUtils.delete(new File(TEST_DIR + COPY), true));
+
+ //Check file list after cleanup
+ File[] afterCleanup = testDir.getAbsoluteFile().getParentFile().listFiles();
+ checkFileLists(beforeCopyFileList, afterCleanup);
+ }
+ }
+
+ /**
+ * Helper method to create a test file with a string content
+ *
+ * @param fileName The fileName to use in the creation
+ * @param test_data The data to store in the file
+ *
+ * @return The File reference
+ */
+ private File createTestFile(String fileName, String test_data)
+ {
+ File test = new File(fileName);
+
+ try
+ {
+ test.createNewFile();
+ //Ensure the JVM cleans up if cleanup failues
+ test.deleteOnExit();
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+
+ BufferedWriter writer = null;
+ try
+ {
+ writer = new BufferedWriter(new FileWriter(test));
+ try
+ {
+ writer.write(test_data);
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ return test;
+ }
+
+ /** Test that deleteFile only deletes the specified file */
+ public void testDeleteFile()
+ {
+ File test = new File("FileUtilsTest-testDelete");
+ //Record file count in parent directory to check it is not changed by delete
+ String path = test.getAbsolutePath();
+ File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountBefore = filesBefore.length;
+
+ try
+ {
+ test.createNewFile();
+ //Ensure the JVM cleans up if cleanup failues
+ test.deleteOnExit();
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+
+ assertTrue("File does not exists", test.exists());
+ assertTrue("File is not a file", test.isFile());
+
+ //Check that file creation can be seen on disk
+ int fileCountCreated = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles().length;
+ assertEquals("File creation was no registered", fileCountBefore + 1, fileCountCreated);
+
+ //Perform Delete
+ assertTrue("Unable to cleanup", FileUtils.deleteFile("FileUtilsTest-testDelete"));
+
+ assertTrue("File exists after delete", !test.exists());
+
+ //Check that after deletion the file count is now accurate
+ File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountAfter = filesAfter.length;
+ assertEquals("File creation was no registered", fileCountBefore, fileCountAfter);
+
+ checkFileLists(filesBefore, filesAfter);
+ }
+
+ /**
+ * Given two lists of File arrays ensure they are the same length and all entries in Before are in After
+ *
+ * @param filesBefore File[]
+ * @param filesAfter File[]
+ */
+ private void checkFileLists(File[] filesBefore, File[] filesAfter)
+ {
+ assertNotNull("Before file list cannot be null", filesBefore);
+ assertNotNull("After file list cannot be null", filesAfter);
+
+ assertEquals("File lists are unequal", filesBefore.length, filesAfter.length);
+
+ for (File fileBefore : filesBefore)
+ {
+ boolean found = false;
+
+ for (File fileAfter : filesAfter)
+ {
+ if (fileBefore.getAbsolutePath().equals(fileAfter.getAbsolutePath()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ assertTrue("File'" + fileBefore.getName() + "' was not in directory afterwards", found);
+ }
+ }
+
+ public void testNonRecursiveNonEmptyDirectoryDeleteFails()
+ {
+ String directoryName = "FileUtilsTest-testRecursiveDelete";
+ File test = new File(directoryName);
+
+ //Record file count in parent directory to check it is not changed by delete
+ String path = test.getAbsolutePath();
+ File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountBefore = filesBefore.length;
+
+ assertTrue("Directory exists", !test.exists());
+
+ test.mkdir();
+
+ //Create a file in the directory
+ String fileName = test.getAbsolutePath() + File.separatorChar + "testFile";
+ File subFile = new File(fileName);
+ try
+ {
+ subFile.createNewFile();
+ //Ensure the JVM cleans up if cleanup failues
+ subFile.deleteOnExit();
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ //Ensure the JVM cleans up if cleanup failues
+ // This must be after the subFile as the directory must be empty before
+ // the delete is performed
+ test.deleteOnExit();
+
+ //Try and delete the non-empty directory
+ assertFalse("Non Empty Directory was successfully deleted.", FileUtils.deleteDirectory(directoryName));
+
+ //Check directory is still there
+ assertTrue("Directory was deleted.", test.exists());
+
+ // Clean up
+ assertTrue("Unable to cleanup", FileUtils.delete(test, true));
+
+ //Check that after deletion the file count is now accurate
+ File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountAfter = filesAfter.length;
+ assertEquals("File creation was no registered", fileCountBefore, fileCountAfter);
+
+ checkFileLists(filesBefore, filesAfter);
+ }
+
+ /** Test that an empty directory can be deleted with deleteDirectory */
+ public void testEmptyDirectoryDelete()
+ {
+ String directoryName = "FileUtilsTest-testRecursiveDelete";
+ File test = new File(directoryName);
+
+ //Record file count in parent directory to check it is not changed by delete
+ String path = test.getAbsolutePath();
+ File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountBefore = filesBefore.length;
+
+ assertTrue("Directory exists", !test.exists());
+
+ test.mkdir();
+ //Ensure the JVM cleans up if cleanup failues
+ test.deleteOnExit();
+
+ //Try and delete the empty directory
+ assertTrue("Non Empty Directory was successfully deleted.", FileUtils.deleteDirectory(directoryName));
+
+ //Check directory is still there
+ assertTrue("Directory was deleted.", !test.exists());
+
+ //Check that after deletion the file count is now accurate
+ File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountAfter = filesAfter.length;
+ assertEquals("File creation was no registered", fileCountBefore, fileCountAfter);
+
+ checkFileLists(filesBefore, filesAfter);
+
+ }
+
+ /** Test that deleteDirectory on a non empty directory to complete */
+ public void testNonEmptyDirectoryDelete()
+ {
+ String directoryName = "FileUtilsTest-testRecursiveDelete";
+ File test = new File(directoryName);
+
+ assertTrue("Directory exists", !test.exists());
+
+ //Record file count in parent directory to check it is not changed by delete
+ String path = test.getAbsolutePath();
+ File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountBefore = filesBefore.length;
+
+ test.mkdir();
+
+ //Create a file in the directory
+ String fileName = test.getAbsolutePath() + File.separatorChar + "testFile";
+ File subFile = new File(fileName);
+ try
+ {
+ subFile.createNewFile();
+ //Ensure the JVM cleans up if cleanup failues
+ subFile.deleteOnExit();
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+
+ // Ensure the JVM cleans up if cleanup failues
+ // This must be after the subFile as the directory must be empty before
+ // the delete is performed
+ test.deleteOnExit();
+
+ //Try and delete the non-empty directory non-recursively
+ assertFalse("Non Empty Directory was successfully deleted.", FileUtils.delete(test, false));
+
+ //Check directory is still there
+ assertTrue("Directory was deleted.", test.exists());
+
+ // Clean up
+ assertTrue("Unable to cleanup", FileUtils.delete(test, true));
+
+ //Check that after deletion the file count is now accurate
+ File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountAfter = filesAfter.length;
+ assertEquals("File creation was no registered", fileCountBefore, fileCountAfter);
+
+ checkFileLists(filesBefore, filesAfter);
+
+ }
+
+ /** Test that a recursive delete successeds */
+ public void testRecursiveDelete()
+ {
+ String directoryName = "FileUtilsTest-testRecursiveDelete";
+ File test = new File(directoryName);
+
+ assertTrue("Directory exists", !test.exists());
+
+ //Record file count in parent directory to check it is not changed by delete
+ String path = test.getAbsolutePath();
+ File[] filesBefore = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountBefore = filesBefore.length;
+
+ test.mkdir();
+
+ createSubDir(directoryName, 2, 4);
+
+ //Ensure the JVM cleans up if cleanup failues
+ // This must be after the sub dir creation as the delete order is
+ // recorded and the directory must be empty to be deleted.
+ test.deleteOnExit();
+
+ assertFalse("Non recursive delete was able to directory", FileUtils.delete(test, false));
+
+ assertTrue("File does not exist after non recursive delete", test.exists());
+
+ assertTrue("Unable to cleanup", FileUtils.delete(test, true));
+
+ assertTrue("File exist after recursive delete", !test.exists());
+
+ //Check that after deletion the file count is now accurate
+ File[] filesAfter = new File(path.substring(0, path.lastIndexOf(File.separator))).listFiles();
+ int fileCountAfter = filesAfter.length;
+ assertEquals("File creation was no registered", fileCountBefore, fileCountAfter);
+
+ checkFileLists(filesBefore, filesAfter);
+
+ }
+
+ private void createSubDir(String path, int directories, int files)
+ {
+ File directory = new File(path);
+
+ assertTrue("Directory" + path + " does not exists", directory.exists());
+
+ for (int dir = 0; dir < directories; dir++)
+ {
+ String subDirName = path + File.separatorChar + "sub" + dir;
+ File subDir = new File(subDirName);
+
+ subDir.mkdir();
+
+ createSubDir(subDirName, directories - 1, files);
+ //Ensure the JVM cleans up if cleanup failues
+ // This must be after the sub dir creation as the delete order is
+ // recorded and the directory must be empty to be deleted.
+ subDir.deleteOnExit();
+ }
+
+ for (int file = 0; file < files; file++)
+ {
+ String subDirName = path + File.separatorChar + "file" + file;
+ File subFile = new File(subDirName);
+ try
+ {
+ subFile.createNewFile();
+ //Ensure the JVM cleans up if cleanup failues
+ subFile.deleteOnExit();
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+ }
+
+}
diff --git a/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/SerialTest.java b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/SerialTest.java
new file mode 100644
index 0000000000..b2578563e0
--- /dev/null
+++ b/RC9/qpid/java/common/src/test/java/org/apache/qpid/util/SerialTest.java
@@ -0,0 +1,82 @@
+package org.apache.qpid.util;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import junit.framework.TestCase;
+
+import java.util.Random;
+
+import org.apache.qpid.SerialException;
+
+/**
+ *Junit tests for the Serial class
+ */
+public class SerialTest extends TestCase
+{
+
+ /**
+ * Test the key boundaries where wraparound occurs.
+ */
+ public void testBoundaries()
+ {
+ assertTrue(Serial.gt(1, 0));
+ assertTrue(Serial.lt(0, 1));
+
+ assertTrue(Serial.gt(Integer.MAX_VALUE+1, Integer.MAX_VALUE));
+ assertTrue(Serial.lt(Integer.MAX_VALUE, Integer.MAX_VALUE+1));
+
+ assertTrue(Serial.gt(0xFFFFFFFF + 1, 0xFFFFFFFF));
+ assertTrue(Serial.lt(0xFFFFFFFF, 0xFFFFFFFF + 1));
+ }
+
+ /**
+ * Test the first Corollary of RFC 1982
+ * For any sequence number s and any integer n such that addition of n
+ * to s is well defined, (s + n) >= s. Further (s + n) == s only when
+ * n == 0, in all other defined cases, (s + n) > s.
+ */
+ public void testCorollary1()
+ {
+ int wrapcount = 0;
+
+ int s = 0;
+
+ for (int i = 0; i < 67108664; i++)
+ {
+ for (int n = 1; n < 4096; n += 512)
+ {
+ assertTrue(Serial.gt(s+n, s));
+ assertTrue(Serial.lt(s, s+n));
+ }
+
+ s += 1024;
+
+ if (s == 0)
+ {
+ wrapcount += 1;
+ }
+ }
+
+ assertTrue(wrapcount > 0);
+ }
+
+}
diff --git a/RC9/qpid/java/common/templates/method/MethodBodyInterface.vm b/RC9/qpid/java/common/templates/method/MethodBodyInterface.vm
new file mode 100644
index 0000000000..d5feba12de
--- /dev/null
+++ b/RC9/qpid/java/common/templates/method/MethodBodyInterface.vm
@@ -0,0 +1,62 @@
+#macro( UpperCamel $name )
+#set( $name = "${name.substring(0,1).toUpperCase()}${name.substring(1)}" )
+#end
+#macro( toUpperCamel $name )${name.substring(0,1).toUpperCase()}${name.substring(1)}#end
+
+
+
+#set( $amqp_ClassName = $amqpClass.Name)
+#UpperCamel( $amqp_ClassName )
+#set( $amqp_MethodName = $amqpMethod.Name )
+#UpperCamel( $amqp_MethodName )
+#set( $javaClassName = "${amqp_ClassName}${amqp_MethodName}Body" )
+
+
+#set( $filename = "${javaClassName}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${generator} - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+
+package org.apache.qpid.framing;
+
+
+public interface ${javaClassName} extends EncodableAMQDataBlock, AMQMethodBody
+{
+
+
+#foreach( $field in $amqpMethod.Fields )
+
+#if( $amqpMethod.isCommon( $field ) )
+ public $field.ConsistentNativeType get#toUpperCamel( ${field.Name} )();
+#end
+#end
+
+
+
+}
diff --git a/RC9/qpid/java/common/templates/method/version/MethodBodyClass.vm b/RC9/qpid/java/common/templates/method/version/MethodBodyClass.vm
new file mode 100644
index 0000000000..9b2ba0fa39
--- /dev/null
+++ b/RC9/qpid/java/common/templates/method/version/MethodBodyClass.vm
@@ -0,0 +1,209 @@
+#macro( UpperCamel $name )
+#set( $name = "${name.substring(0,1).toUpperCase()}${name.substring(1)}" )
+#end
+#macro( toUpperCamel $name )${name.substring(0,1).toUpperCase()}${name.substring(1)}#end
+
+
+
+#set( $amqp_ClassName = $amqpClass.Name)
+#UpperCamel( $amqp_ClassName )
+#set( $amqp_MethodName = $amqpMethod.Name )
+#UpperCamel( $amqp_MethodName )
+#set( $javaClassName = "${amqp_ClassName}${amqp_MethodName}BodyImpl" )
+#set( $interfaceName = "${amqp_ClassName}${amqp_MethodName}Body" )
+#set( $amqpPackageName = "amqp_$version.getMajor()_$version.getMinor()" )
+
+#set( $filename = "${amqpPackageName}/${javaClassName}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${generator} - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+#set( $clazz = $amqpClass.asSingleVersionClass( $version ) )
+#set( $method = $amqpMethod.asSingleVersionMethod( $version ) )
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import java.util.HashMap;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.AMQException;
+
+public class ${javaClassName} extends AMQMethodBody_$version.getMajor()_$version.getMinor() implements $interfaceName
+{
+ private static final AMQMethodBodyInstanceFactory FACTORY_INSTANCE = new AMQMethodBodyInstanceFactory()
+ {
+ public AMQMethodBody newInstance(ByteBuffer in, long size) throws AMQFrameDecodingException
+ {
+ return new ${javaClassName}(in);
+ }
+
+
+ };
+
+
+ public static AMQMethodBodyInstanceFactory getFactory()
+ {
+ return FACTORY_INSTANCE;
+ }
+
+ public static int CLASS_ID = $clazz.ClassId;
+
+ public static int METHOD_ID = $method.MethodId;
+
+
+
+ // Fields declared in specification
+#foreach( $field in $method.ConsolidatedFields )
+ private final $field.NativeType _$field.getName(); // $field.UnderlyingFields
+#end
+
+
+ // Constructor
+
+ public ${javaClassName}(ByteBuffer buffer) throws AMQFrameDecodingException
+ {
+#foreach( $field in $method.ConsolidatedFields )
+ _$field.Name = read$field.getEncodingType()( buffer );
+#end
+ }
+
+ public ${javaClassName}(
+#foreach( $field in $method.FieldList )
+#if( $velocityCount == $method.getFieldList().size() )
+ $field.NativeType $field.Name
+#else
+ $field.NativeType $field.Name,
+#end
+#end
+ )
+ {
+#set( $consolidatedFieldName = "" )
+#foreach( $field in $method.FieldList )
+#if( $method.isConsolidated( $field.Name ) )
+#if( !$method.getConsolidatedFieldName( $field.Name ).equals( $consolidatedFieldName ) )
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName; // 1
+#end
+#set( $consolidatedFieldName = $method.getConsolidatedFieldName( $field.Name ) )
+ byte $consolidatedFieldName = (byte)0;
+#end
+ if( $field.Name )
+ {
+ $consolidatedFieldName = (byte) (((int) $consolidatedFieldName) | (1 << $method.getPositionInBitField( $field.Name )));
+ }
+#if( $velocityCount == $method.getFieldList().size())
+ _$consolidatedFieldName = $consolidatedFieldName;
+#else
+
+#end
+#else
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName;
+#end
+#set( $consolidatedFieldName = "" )
+ _$field.Name = $field.Name;
+#end
+#end
+ }
+
+ public int getClazz()
+ {
+ return CLASS_ID;
+ }
+
+ public int getMethod()
+ {
+ return METHOD_ID;
+ }
+
+
+#foreach( $field in $method.FieldList )
+ public final $field.NativeType get#toUpperCamel( ${field.Name} )()
+ {
+#if( $method.isConsolidated( $field.Name ) )
+ return (((int)(_$method.getConsolidatedFieldName( $field.Name ))) & ( 1 << $method.getPositionInBitField( $field.Name ))) != 0;
+#else
+ return _$field.Name;
+#end
+ }
+#end
+
+ protected int getBodySize()
+ {
+#set( $fixedSize = 0 )
+#foreach( $field in $method.ConsolidatedFields )
+#if( $field.isFixedSize() )
+#set( $fixedSize = $fixedSize + $field.Size )
+#end
+#end
+ int size = $fixedSize;
+#foreach( $field in $method.ConsolidatedFields )
+#if( ! $field.isFixedSize() )
+ size += getSizeOf( _$field.Name );
+#end
+#end
+ return size;
+ }
+
+ public void writeMethodPayload(ByteBuffer buffer)
+ {
+#foreach( $field in $method.ConsolidatedFields )
+ write$field.getEncodingType()( buffer, _$field.Name );
+#end
+ }
+
+ public boolean execute(MethodDispatcher dispatcher, int channelId) throws AMQException
+ {
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+#if( $amqpMethod.inAllVersions() )
+ return dispatcher.dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(this, channelId);
+#else
+ return ((MethodDispatcher_$version.getMajor()_$version.getMinor())dispatcher).dispatch${amqp_ClassName}${amqpMethodNameUpperCamel}(this, channelId);
+
+#end
+
+ }
+
+
+ public String toString()
+ {
+ StringBuilder buf = new StringBuilder("[$javaClassName: ");
+#foreach( $field in $method.FieldList )
+ buf.append( "$field.Name=" );
+ buf.append( get#toUpperCamel( $field.Name )() );
+#if( $velocityCount != $method.FieldList.size() )
+ buf.append( ", " );
+#end
+#end
+ buf.append("]");
+ return buf.toString();
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/templates/model/ClientMethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/ClientMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..9e4aee7dee
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/ClientMethodDispatcherInterface.vm
@@ -0,0 +1,56 @@
+#set( $filename = "ClientMethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface ClientMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.inAllVersions() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isClientMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/MethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/MethodDispatcherInterface.vm
new file mode 100644
index 0000000000..ff14715fef
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/MethodDispatcherInterface.vm
@@ -0,0 +1,39 @@
+#set( $filename = "MethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface MethodDispatcher extends
+ ClientMethodDispatcher, ServerMethodDispatcher
+{
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/MethodRegistryClass.vm b/RC9/qpid/java/common/templates/model/MethodRegistryClass.vm
new file mode 100644
index 0000000000..759e5e4a42
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/MethodRegistryClass.vm
@@ -0,0 +1,104 @@
+#set( $filename = "MethodRegistry.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+import java.util.Map;
+import java.util.HashMap;
+
+
+public abstract class MethodRegistry
+{
+ private static final Map<ProtocolVersion, MethodRegistry> _registries =
+ new HashMap<ProtocolVersion, MethodRegistry>();
+
+#foreach( $supportedVersion in $model.VersionSet )
+
+#set( $verName = "_$supportedVersion.getMajor()_$supportedVersion.getMinor()" )
+#set( $regPackage = "org.apache.qpid.framing.amqp$verName" )
+ public static final MethodRegistry registry_$supportedVersion.getMajor()_$supportedVersion.getMinor() =
+ new ${regPackage}.MethodRegistry${verName}();
+
+#end
+
+
+ public abstract AMQMethodBody convertToBody(ByteBuffer in, long size)
+ throws AMQFrameDecodingException;
+
+ public abstract int getMaxClassId();
+
+ public abstract int getMaxMethodId(int classId);
+
+
+ protected MethodRegistry(ProtocolVersion pv)
+ {
+ _registries.put(pv, this);
+ }
+
+ public static MethodRegistry getMethodRegistry(ProtocolVersion pv)
+ {
+ return _registries.get(pv);
+ }
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.isVersionInterfaceConsistent() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public abstract ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body create${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ final $field.NativeType $field.Name
+#else
+ final $field.NativeType $field.Name,
+#end
+#end
+ );
+
+
+#end
+#end
+#end
+
+
+ public abstract ProtocolVersionMethodConverter getProtocolVersionMethodConverter();
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/ProtocolVersionListClass.vm b/RC9/qpid/java/common/templates/model/ProtocolVersionListClass.vm
new file mode 100644
index 0000000000..9ac6adfdf5
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/ProtocolVersionListClass.vm
@@ -0,0 +1,178 @@
+#set( $filename = "ProtocolVersion.java" )
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+
+/*
+* This file is auto-generated by $generator - do not modify.
+* Supported AMQP versions:
+#foreach( $version in $model.getVersionSet() )
+* $version.getMajor()-$version.getMinor()
+#end
+*/
+
+package org.apache.qpid.framing;
+
+import java.util.SortedSet;
+import java.util.Collections;
+import java.util.TreeSet;
+import java.util.Map;
+import java.util.HashMap;
+
+
+public class ProtocolVersion implements Comparable
+{
+ private final byte _majorVersion;
+ private final byte _minorVersion;
+
+
+ public ProtocolVersion(byte majorVersion, byte minorVersion)
+ {
+ _majorVersion = majorVersion;
+ _minorVersion = minorVersion;
+ }
+
+ public byte getMajorVersion()
+ {
+ return _majorVersion;
+ }
+
+ public byte getMinorVersion()
+ {
+ return _minorVersion;
+ }
+
+
+ public int compareTo(Object o)
+ {
+ ProtocolVersion pv = (ProtocolVersion) o;
+
+ /*
+ * 0-8 has it's major and minor numbers the wrong way round (it's actually 8-0)...
+ * so we need to deal with that case specially
+ */
+
+ if((_majorVersion == (byte) 8) && (_minorVersion == (byte) 0))
+ {
+ ProtocolVersion fixedThis = new ProtocolVersion(_minorVersion, _majorVersion);
+ return fixedThis.compareTo(pv);
+ }
+
+ if((pv.getMajorVersion() == (byte) 8) && (pv.getMinorVersion() == (byte) 0))
+ {
+ ProtocolVersion fixedOther = new ProtocolVersion(pv.getMinorVersion(), pv.getMajorVersion());
+ return this.compareTo(fixedOther);
+ }
+
+ if(_majorVersion > pv.getMajorVersion())
+ {
+ return 1;
+ }
+ else if(_majorVersion < pv.getMajorVersion())
+ {
+ return -1;
+ }
+ else if(_minorVersion > pv.getMinorVersion())
+ {
+ return 1;
+ }
+ else if(getMinorVersion() < pv.getMinorVersion())
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+
+ }
+
+ public boolean equals(Object o)
+ {
+ return o != null && (o == this || (compareTo(o) == 0));
+ }
+
+ public int hashCode()
+ {
+ return (0xFF & (int)_minorVersion) | ((0xFF & (int)_majorVersion) << 8);
+ }
+
+
+ public boolean isSupported()
+ {
+ return _supportedVersions.contains(this);
+ }
+
+ public static ProtocolVersion getLatestSupportedVersion()
+ {
+ return _supportedVersions.last();
+ }
+
+ private static final SortedSet<ProtocolVersion> _supportedVersions;
+ private static final Map<String, ProtocolVersion> _nameToVersionMap =
+ new HashMap<String, ProtocolVersion>();
+ private static final ProtocolVersion _defaultVersion;
+
+
+#foreach( $version in $model.getVersionSet() )
+#set( $versionId = "v$version.getMajor()_$version.getMinor()" )
+ public static final ProtocolVersion $versionId = new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor());
+#end
+
+ static
+ {
+ SortedSet<ProtocolVersion> versions = new TreeSet<ProtocolVersion>();
+
+#foreach( $version in $model.getVersionSet() )
+#set( $versionId = "v$version.getMajor()_$version.getMinor()" )
+ versions.add($versionId);
+ _nameToVersionMap.put("${version.getMajor()}-${version.getMinor()}", $versionId);
+#end
+ _supportedVersions = Collections.unmodifiableSortedSet(versions);
+
+
+ ProtocolVersion systemDefinedVersion =
+ _nameToVersionMap.get(System.getProperty("org.apache.qpid.amqp_version"));
+
+ _defaultVersion = (systemDefinedVersion == null)
+ ? getLatestSupportedVersion()
+ : systemDefinedVersion;
+ }
+
+
+ public static SortedSet<ProtocolVersion> getSupportedProtocolVersions()
+ {
+ return _supportedVersions;
+ }
+
+
+
+ public static ProtocolVersion parse(String name)
+ {
+ return _nameToVersionMap.get(name);
+ }
+
+ public static ProtocolVersion defaultProtocolVersion()
+ {
+ return _defaultVersion;
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/templates/model/ServerMethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/ServerMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..b80d6027b7
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/ServerMethodDispatcherInterface.vm
@@ -0,0 +1,56 @@
+#set( $filename = "ServerMethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface ServerMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.inAllVersions() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isServerMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/version/AmqpConstantsClass.vm b/RC9/qpid/java/common/templates/model/version/AmqpConstantsClass.vm
new file mode 100644
index 0000000000..8d459f2977
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/version/AmqpConstantsClass.vm
@@ -0,0 +1,37 @@
+&{AmqpConstants.java}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+package org.apache.qpid.framing;
+
+class AmqpConstants
+{
+ // Constant getValue methods
+
+%{TLIST} ${const_get_method}
+
+}
diff --git a/RC9/qpid/java/common/templates/model/version/ClientMethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/version/ClientMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..80705c1a39
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/version/ClientMethodDispatcherInterface.vm
@@ -0,0 +1,55 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()} extends ClientMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isClientMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/version/MethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/version/MethodDispatcherInterface.vm
new file mode 100644
index 0000000000..8a7b667a91
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/version/MethodDispatcherInterface.vm
@@ -0,0 +1,43 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/MethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface MethodDispatcher_${version.getMajor()}_${version.getMinor()}
+ extends MethodDispatcher,
+ ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()},
+ ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()}
+{
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templates/model/version/MethodRegistryClass.vm b/RC9/qpid/java/common/templates/model/version/MethodRegistryClass.vm
new file mode 100644
index 0000000000..277605e34b
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/version/MethodRegistryClass.vm
@@ -0,0 +1,193 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/MethodRegistry_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+package org.apache.qpid.framing.amqp_${version.getMajor()}_${version.getMinor()};
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.protocol.AMQConstant;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+
+public class MethodRegistry_$version.getMajor()_$version.getMinor() extends MethodRegistry
+{
+
+ private static final Logger _log = LoggerFactory.getLogger(MethodRegistry.class);
+
+ private ProtocolVersionMethodConverter _protocolVersionConverter = new MethodConverter_$version.getMajor()_$version.getMinor()();
+
+#set( $specificModel = $model.asSingleVersionModel() )
+
+
+#set( $maxClassId = $specificModel.getMaximumClassId()+1 )
+ private final AMQMethodBodyInstanceFactory[][] _factories = new AMQMethodBodyInstanceFactory[$maxClassId][];
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()()
+ {
+ this(new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()));
+ }
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()(ProtocolVersion pv)
+ {
+ super(pv);
+#foreach( $amqpClass in $specificModel.getClassList() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+
+
+ // Register method body instance factories for the $amqpClassNameUpperCamel class.
+
+#set( $maxMethodId = $amqpClass.getMaximumMethodId()+1 )
+ _factories[$amqpClass.getClassId()] = new AMQMethodBodyInstanceFactory[$maxMethodId];
+
+#foreach( $amqpMethod in $amqpClass.getMethodList() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ _factories[$amqpClass.getClassId()][$amqpMethod.getMethodId()] = ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}BodyImpl.getFactory();
+#end
+
+#end
+
+
+ }
+
+
+ public AMQMethodBody convertToBody(ByteBuffer in, long size)
+ throws AMQFrameDecodingException
+ {
+ int classId = in.getUnsignedShort();
+ int methodId = in.getUnsignedShort();
+
+ AMQMethodBodyInstanceFactory bodyFactory;
+ try
+ {
+ bodyFactory = _factories[classId][methodId];
+ }
+ catch(NullPointerException e)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+ catch(IndexOutOfBoundsException e)
+ {
+ if(classId >= _factories.length)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ }
+
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+
+
+ return bodyFactory.newInstance(in, size);
+
+
+ }
+
+
+ public int getMaxClassId()
+ {
+ return $specificModel.getMaximumClassId();
+ }
+
+ public int getMaxMethodId(int classId)
+ {
+ return _factories[classId].length - 1;
+ }
+
+
+
+#foreach( $amqpClass in $specificModel.getClassList() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+
+#foreach( $amqpMethod in $amqpClass.getMethodList() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body create${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ final $field.NativeType $field.Name
+#else
+ final $field.NativeType $field.Name,
+#end
+#end
+ )
+ {
+ return new ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}BodyImpl(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ $field.Name
+#else
+ $field.Name,
+#end
+#end
+ );
+ }
+
+#end
+
+#end
+
+
+ public ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
+ {
+ return _protocolVersionConverter;
+ }
+
+
+}
diff --git a/RC9/qpid/java/common/templates/model/version/ServerMethodDispatcherInterface.vm b/RC9/qpid/java/common/templates/model/version/ServerMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..db388fcc65
--- /dev/null
+++ b/RC9/qpid/java/common/templates/model/version/ServerMethodDispatcherInterface.vm
@@ -0,0 +1,55 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()} extends ServerMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isServerMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/RC9/qpid/java/common/templating.py b/RC9/qpid/java/common/templating.py
new file mode 100644
index 0000000000..732e96fa60
--- /dev/null
+++ b/RC9/qpid/java/common/templating.py
@@ -0,0 +1,119 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+class Parser:
+
+ def __init__(self, **kwargs):
+ self.output = ""
+ self.environ = {"out": self.parse}
+ for k, v in kwargs.items():
+ self.environ[k] = v
+ self.text = ""
+ self.level = 0
+ self.line = None
+
+ def action(self, actor):
+ text = self.text
+ self.text = ""
+ actor(text)
+
+ def out(self, text):
+ self.output += text
+
+ def prefix_lines(self, text):
+ return "%s%s" % ("\n"*(self.line - 1 - text.count("\n")), text)
+
+ def evaluate(self, text):
+ self.out(str(eval(self.prefix_lines(text), self.environ, self.environ)))
+
+ def execute(self, text):
+ exec self.prefix_lines(text) in self.environ, self.environ
+
+ def parse(self, input):
+ old_line = self.line
+ try:
+ state = self.start
+ self.line = 1
+ for ch in input:
+ state = state(ch)
+ if ch == "\n":
+ self.line += 1
+ if state == self.start:
+ self.action(self.out)
+ elif state == self.alnum:
+ self.action(self.evaluate)
+ else:
+ raise ParseError()
+ finally:
+ self.line = old_line
+
+ def start(self, ch):
+ if ch == "$":
+ return self.dollar
+ else:
+ self.text += ch
+ return self.start
+
+ def dollar(self, ch):
+ if ch == "$":
+ self.text += "$"
+ return self.start
+ elif ch == "(":
+ self.action(self.out)
+ return self.expression
+ elif ch == "{":
+ self.action(self.out)
+ return self.block
+ else:
+ self.action(self.out)
+ self.text += ch
+ return self.alnum
+
+ def alnum(self, ch):
+ if ch.isalnum():
+ self.text += ch
+ return self.alnum
+ else:
+ self.action(self.evaluate)
+ self.text += ch
+ return self.start
+
+ def match(self, ch, start, end):
+ if ch == start:
+ self.level += 1
+ if ch == end:
+ self.level -= 1
+
+ def block(self, ch):
+ if not self.level and ch == "}":
+ self.action(self.execute)
+ return self.start
+ else:
+ self.match(ch, "{", "}")
+ self.text += ch
+ return self.block
+
+ def expression(self, ch):
+ if not self.level and ch == ")":
+ self.action(self.evaluate)
+ return self.start
+ else:
+ self.match(ch, "(", ")")
+ self.text += ch
+ return self.expression