blob: 32bed2b381712c4f911dddb484ce1395e3405f02 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.qpid.proton.codec.transport;
import java.util.Collection;
import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.UnsignedLong;
import org.apache.qpid.proton.amqp.messaging.Accepted;
import org.apache.qpid.proton.amqp.messaging.Released;
import org.apache.qpid.proton.amqp.transport.DeliveryState;
import org.apache.qpid.proton.amqp.transport.Disposition;
import org.apache.qpid.proton.amqp.transport.Role;
import org.apache.qpid.proton.codec.AMQPType;
import org.apache.qpid.proton.codec.DecodeException;
import org.apache.qpid.proton.codec.Decoder;
import org.apache.qpid.proton.codec.DecoderImpl;
import org.apache.qpid.proton.codec.EncoderImpl;
import org.apache.qpid.proton.codec.EncodingCodes;
import org.apache.qpid.proton.codec.FastPathDescribedTypeConstructor;
import org.apache.qpid.proton.codec.TypeEncoding;
import org.apache.qpid.proton.codec.WritableBuffer;
public class FastPathDispositionType implements AMQPType<Disposition>, FastPathDescribedTypeConstructor<Disposition> {
private static final byte DESCRIPTOR_CODE = 0x15;
private static final byte ACCEPTED_DESCRIPTOR_CODE = 0x24;
private static final Object[] DESCRIPTORS =
{
UnsignedLong.valueOf(DESCRIPTOR_CODE), Symbol.valueOf("amqp:disposition:list"),
};
private static final byte[] ACCEPTED_ENCODED_BYTES = new byte[] {
EncodingCodes.DESCRIBED_TYPE_INDICATOR,
EncodingCodes.SMALLULONG,
ACCEPTED_DESCRIPTOR_CODE,
EncodingCodes.LIST0
};
private final DispositionType dispositionType;
public FastPathDispositionType(EncoderImpl encoder) {
this.dispositionType = new DispositionType(encoder);
}
public EncoderImpl getEncoder() {
return dispositionType.getEncoder();
}
public DecoderImpl getDecoder() {
return dispositionType.getDecoder();
}
@Override
public boolean encodesJavaPrimitive() {
return false;
}
@Override
public Class<Disposition> getTypeClass() {
return Disposition.class;
}
@Override
public TypeEncoding<Disposition> getEncoding(Disposition disposition) {
return dispositionType.getEncoding(disposition);
}
@Override
public TypeEncoding<Disposition> getCanonicalEncoding() {
return dispositionType.getCanonicalEncoding();
}
@Override
public Collection<? extends TypeEncoding<Disposition>> getAllEncodings() {
return dispositionType.getAllEncodings();
}
@Override
public Disposition readValue() {
DecoderImpl decoder = getDecoder();
byte typeCode = decoder.getBuffer().get();
@SuppressWarnings("unused")
int size = 0;
int count = 0;
switch (typeCode) {
case EncodingCodes.LIST0:
// TODO - Technically invalid however old decoder also allowed this.
break;
case EncodingCodes.LIST8:
size = ((int)decoder.getBuffer().get()) & 0xff;
count = ((int)decoder.getBuffer().get()) & 0xff;
break;
case EncodingCodes.LIST32:
size = decoder.getBuffer().getInt();
count = decoder.getBuffer().getInt();
break;
default:
throw new DecodeException("Incorrect type found in Disposition encoding: " + typeCode);
}
Disposition disposition = new Disposition();
for (int index = 0; index < count; ++index) {
switch (index) {
case 0:
disposition.setRole(Boolean.TRUE.equals(decoder.readBoolean()) ? Role.RECEIVER : Role.SENDER);
break;
case 1:
disposition.setFirst(decoder.readUnsignedInteger(null));
break;
case 2:
disposition.setLast(decoder.readUnsignedInteger(null));
break;
case 3:
disposition.setSettled(decoder.readBoolean(false));
break;
case 4:
disposition.setState((DeliveryState) decoder.readObject());
break;
case 5:
disposition.setBatchable(decoder.readBoolean(false));
break;
default:
throw new IllegalStateException("To many entries in Disposition encoding");
}
}
return disposition;
}
@Override
public void skipValue() {
getDecoder().readConstructor().skipValue();
}
@Override
public void write(Disposition disposition) {
WritableBuffer buffer = getEncoder().getBuffer();
int count = getElementCount(disposition);
byte encodingCode = deduceEncodingCode(disposition, count);
buffer.put(EncodingCodes.DESCRIBED_TYPE_INDICATOR);
buffer.put(EncodingCodes.SMALLULONG);
buffer.put(DESCRIPTOR_CODE);
buffer.put(encodingCode);
final int fieldWidth;
if (encodingCode == EncodingCodes.LIST8) {
fieldWidth = 1;
} else {
fieldWidth = 4;
}
int startIndex = buffer.position();
// Reserve space for the size and write the count of list elements.
if (fieldWidth == 1) {
buffer.put((byte) 0);
buffer.put((byte) count);
} else {
buffer.putInt(0);
buffer.putInt(count);
}
// Write the list elements and then compute total size written.
for (int i = 0; i < count; ++i) {
writeElement(disposition, i);
}
// Move back and write the size
int endIndex = buffer.position();
int writeSize = endIndex - startIndex - fieldWidth;
buffer.position(startIndex);
if (fieldWidth == 1) {
buffer.put((byte) writeSize);
} else {
buffer.putInt(writeSize);
}
buffer.position(endIndex);
}
private void writeElement(Disposition disposition, int index) {
switch (index) {
case 0:
getEncoder().writeBoolean(disposition.getRole().getValue());
break;
case 1:
getEncoder().writeUnsignedInteger(disposition.getFirst());
break;
case 2:
getEncoder().writeUnsignedInteger(disposition.getLast());
break;
case 3:
getEncoder().writeBoolean(disposition.getSettled());
break;
case 4:
if (Accepted.getInstance().equals(disposition.getState())) {
getEncoder().getBuffer().put(ACCEPTED_ENCODED_BYTES, 0, ACCEPTED_ENCODED_BYTES.length);
} else {
getEncoder().writeObject(disposition.getState());
}
break;
case 5:
getEncoder().writeBoolean(disposition.getBatchable());
break;
default:
throw new IllegalArgumentException("Unknown Disposition value index: " + index);
}
}
private int getElementCount(Disposition disposition) {
if (disposition.getBatchable()) {
return 6;
} else if (disposition.getState() != null) {
return 5;
} else if (disposition.getSettled()) {
return 4;
} else if (disposition.getLast() != null) {
return 3;
} else {
return 2;
}
}
private byte deduceEncodingCode(Disposition value, int elementCount) {
if (value.getState() == null) {
return EncodingCodes.LIST8;
} else if (value.getState() == Accepted.getInstance() || value.getState() == Released.getInstance()) {
return EncodingCodes.LIST8;
} else {
return EncodingCodes.LIST32;
}
}
public static void register(Decoder decoder, EncoderImpl encoder) {
FastPathDispositionType type = new FastPathDispositionType(encoder);
for(Object descriptor : DESCRIPTORS) {
decoder.register(descriptor, (FastPathDescribedTypeConstructor<?>) type);
}
encoder.register(type);
}
}