blob: 3f1baf1dce7b5d94d2ea9e26963f03f37a906d83 [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.activemq.artemis.utils.collections;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import io.netty.buffer.ByteBuf;
import org.apache.activemq.artemis.api.core.ActiveMQPropertyConversionException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.logs.ActiveMQUtilBundle;
import org.apache.activemq.artemis.utils.AbstractByteBufPool;
import org.apache.activemq.artemis.utils.ByteUtil;
import org.apache.activemq.artemis.utils.DataConstants;
import static org.apache.activemq.artemis.utils.DataConstants.BOOLEAN;
import static org.apache.activemq.artemis.utils.DataConstants.BYTE;
import static org.apache.activemq.artemis.utils.DataConstants.BYTES;
import static org.apache.activemq.artemis.utils.DataConstants.CHAR;
import static org.apache.activemq.artemis.utils.DataConstants.DOUBLE;
import static org.apache.activemq.artemis.utils.DataConstants.FLOAT;
import static org.apache.activemq.artemis.utils.DataConstants.INT;
import static org.apache.activemq.artemis.utils.DataConstants.LONG;
import static org.apache.activemq.artemis.utils.DataConstants.NULL;
import static org.apache.activemq.artemis.utils.DataConstants.SHORT;
import static org.apache.activemq.artemis.utils.DataConstants.STRING;
/**
* Property Value Conversion.
* <p>
* This implementation follows section 3.5.4 of the <i>Java Message Service</i> specification
* (Version 1.1 April 12, 2002).
* <p>
*/
public class TypedProperties {
private Map<SimpleString, PropertyValue> properties;
private int size;
private final Predicate<SimpleString> internalPropertyPredicate;
private boolean internalProperties;
public TypedProperties() {
this.internalPropertyPredicate = null;
}
public TypedProperties(Predicate<SimpleString> internalPropertyPredicate) {
this.internalPropertyPredicate = internalPropertyPredicate;
}
/**
* Return the number of properties
* */
public synchronized int size() {
return properties == null ? 0 : properties.size();
}
public synchronized int getMemoryOffset() {
// The estimate is basically the encode size + 2 object references for each entry in the map
// Note we don't include the attributes or anything else since they already included in the memory estimate
// of the ServerMessage
return properties == null ? 0 : size + 2 * DataConstants.SIZE_INT * properties.size();
}
public TypedProperties(final TypedProperties other) {
synchronized (other) {
properties = other.properties == null ? null : new HashMap<>(other.properties);
size = other.size;
internalPropertyPredicate = other.internalPropertyPredicate;
internalProperties = other.internalProperties;
}
}
public void putBooleanProperty(final SimpleString key, final boolean value) {
doPutValue(key, BooleanValue.of(value));
}
public void putByteProperty(final SimpleString key, final byte value) {
doPutValue(key, ByteValue.valueOf(value));
}
public void putBytesProperty(final SimpleString key, final byte[] value) {
doPutValue(key, value == null ? NullValue.INSTANCE : new BytesValue(value));
}
public void putShortProperty(final SimpleString key, final short value) {
doPutValue(key, new ShortValue(value));
}
public void putIntProperty(final SimpleString key, final int value) {
doPutValue(key, new IntValue(value));
}
public void putLongProperty(final SimpleString key, final long value) {
doPutValue(key, new LongValue(value));
}
public void putFloatProperty(final SimpleString key, final float value) {
doPutValue(key, new FloatValue(value));
}
public void putDoubleProperty(final SimpleString key, final double value) {
doPutValue(key, new DoubleValue(value));
}
public void putSimpleStringProperty(final SimpleString key, final SimpleString value) {
doPutValue(key, value == null ? NullValue.INSTANCE : new StringValue(value));
}
public void putNullValue(final SimpleString key) {
doPutValue(key, NullValue.INSTANCE);
}
public void putCharProperty(final SimpleString key, final char value) {
doPutValue(key, new CharValue(value));
}
public void putTypedProperties(final TypedProperties otherProps) {
if (otherProps == null || otherProps == this || otherProps.properties == null) {
return;
}
otherProps.forEachInternal(this::doPutValue);
}
public TypedProperties putProperty(final SimpleString key, final Object value) {
setObjectProperty(key, value, this);
return this;
}
public Object getProperty(final SimpleString key) {
return doGetProperty(key);
}
public Boolean getBooleanProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Boolean.valueOf(null);
} else if (value instanceof Boolean) {
return (Boolean) value;
} else if (value instanceof SimpleString) {
return Boolean.valueOf(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Byte getByteProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Byte.valueOf(null);
} else if (value instanceof Byte) {
return (Byte) value;
} else if (value instanceof SimpleString) {
return Byte.parseByte(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Character getCharProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
throw new NullPointerException("Invalid conversion: " + key);
}
if (value instanceof Character) {
return ((Character) value);
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public byte[] getBytesProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return null;
} else if (value instanceof byte[]) {
return (byte[]) value;
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Double getDoubleProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Double.valueOf(null);
} else if (value instanceof Float) {
return ((Float) value).doubleValue();
} else if (value instanceof Double) {
return (Double) value;
} else if (value instanceof SimpleString) {
return Double.parseDouble(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Integer getIntProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Integer.valueOf(null);
} else if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Byte) {
return ((Byte) value).intValue();
} else if (value instanceof Short) {
return ((Short) value).intValue();
} else if (value instanceof SimpleString) {
return Integer.parseInt(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Long getLongProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Long.valueOf(null);
} else if (value instanceof Long) {
return (Long) value;
} else if (value instanceof Byte) {
return ((Byte) value).longValue();
} else if (value instanceof Short) {
return ((Short) value).longValue();
} else if (value instanceof Integer) {
return ((Integer) value).longValue();
} else if (value instanceof SimpleString) {
return Long.parseLong(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Short getShortProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return Short.valueOf(null);
} else if (value instanceof Byte) {
return ((Byte) value).shortValue();
} else if (value instanceof Short) {
return (Short) value;
} else if (value instanceof SimpleString) {
return Short.parseShort(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Float getFloatProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null)
return Float.valueOf(null);
if (value instanceof Float) {
return ((Float) value);
}
if (value instanceof SimpleString) {
return Float.parseFloat(((SimpleString) value).toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public SimpleString getSimpleStringProperty(final SimpleString key) throws ActiveMQPropertyConversionException {
Object value = doGetProperty(key);
if (value == null) {
return null;
}
if (value instanceof SimpleString) {
return (SimpleString) value;
} else if (value instanceof Boolean) {
return new SimpleString(value.toString());
} else if (value instanceof Character) {
return new SimpleString(value.toString());
} else if (value instanceof Byte) {
return new SimpleString(value.toString());
} else if (value instanceof Short) {
return new SimpleString(value.toString());
} else if (value instanceof Integer) {
return new SimpleString(value.toString());
} else if (value instanceof Long) {
return new SimpleString(value.toString());
} else if (value instanceof Float) {
return new SimpleString(value.toString());
} else if (value instanceof Double) {
return new SimpleString(value.toString());
}
throw new ActiveMQPropertyConversionException("Invalid conversion: " + key);
}
public Object removeProperty(final SimpleString key) {
return doRemoveProperty(key);
}
public synchronized boolean containsProperty(final SimpleString key) {
if (properties == null) {
return false;
} else {
return properties.containsKey(key);
}
}
public synchronized Set<SimpleString> getPropertyNames() {
if (properties == null) {
return Collections.emptySet();
} else {
return new HashSet<>(properties.keySet());
}
}
public synchronized boolean clearInternalProperties() {
return internalProperties && removeInternalProperties();
}
private synchronized boolean removeInternalProperties() {
if (internalPropertyPredicate == null) {
return false;
}
if (properties == null) {
return false;
}
if (properties.isEmpty()) {
return false;
}
int removedBytes = 0;
boolean removed = false;
final Iterator<Entry<SimpleString, PropertyValue>> keyNameIterator = properties.entrySet().iterator();
while (keyNameIterator.hasNext()) {
final Entry<SimpleString, PropertyValue> entry = keyNameIterator.next();
final SimpleString propertyName = entry.getKey();
if (internalPropertyPredicate.test(propertyName)) {
final PropertyValue propertyValue = entry.getValue();
removedBytes += propertyName.sizeof() + propertyValue.encodeSize();
keyNameIterator.remove();
removed = true;
}
}
internalProperties = false;
size -= removedBytes;
return removed;
}
public synchronized void forEachKey(Consumer<SimpleString> action) {
if (properties != null) {
properties.keySet().forEach(action::accept);
}
}
public synchronized void forEach(BiConsumer<SimpleString, Object> action) {
if (properties != null) {
properties.forEach((k, v) -> action.accept(k, v.getValue()));
}
}
private synchronized void forEachInternal(BiConsumer<SimpleString, PropertyValue> action) {
if (properties != null) {
properties.forEach(action::accept);
}
}
/**
* Performs a search among the valid key properties contained in {@code buffer}, starting from {@code from}
* assuming it to be a valid encoded {@link TypedProperties} content.
*
* @throws IllegalStateException if any not-valid property is found while searching the {@code key} property
*/
public static boolean searchProperty(SimpleString key, ByteBuf buffer, int startIndex) {
// It won't implement a straight linear search for key
// because it would risk to find a SimpleString encoded property value
// equals to the key we're searching for!
int index = startIndex;
byte b = buffer.getByte(index);
index++;
if (b == DataConstants.NULL) {
return false;
}
final int numHeaders = buffer.getInt(index);
index += Integer.BYTES;
for (int i = 0; i < numHeaders; i++) {
final int keyLength = buffer.getInt(index);
index += Integer.BYTES;
if (key.equals(buffer, index, keyLength)) {
return true;
}
if (i == numHeaders - 1) {
return false;
}
index += keyLength;
byte type = buffer.getByte(index);
index++;
switch (type) {
case NULL: {
break;
}
case CHAR:
case SHORT: {
index += Short.BYTES;
break;
}
case BOOLEAN:
case BYTE: {
index += Byte.BYTES;
break;
}
case BYTES:
case STRING: {
index += (Integer.BYTES + buffer.getInt(index));
break;
}
case INT: {
index += Integer.BYTES;
break;
}
case LONG: {
index += Long.BYTES;
break;
}
case FLOAT: {
index += Float.BYTES;
break;
}
case DOUBLE: {
index += Double.BYTES;
break;
}
default: {
throw ActiveMQUtilBundle.BUNDLE.invalidType(type);
}
}
}
return false;
}
public synchronized void decode(final ByteBuf buffer,
final TypedPropertiesDecoderPools keyValuePools) {
byte b = buffer.readByte();
if (b == DataConstants.NULL) {
properties = null;
size = 0;
} else {
int numHeaders = buffer.readInt();
//optimize the case of no collisions to avoid any resize (it doubles the map size!!!) when load factor is reached
properties = new HashMap<>(numHeaders, 1.0f);
size = 0;
for (int i = 0; i < numHeaders; i++) {
final SimpleString key = SimpleString.readSimpleString(buffer, keyValuePools == null ? null : keyValuePools.getPropertyKeysPool());
byte type = buffer.readByte();
PropertyValue val;
switch (type) {
case NULL: {
val = NullValue.INSTANCE;
doPutValue(key, val);
break;
}
case CHAR: {
val = new CharValue(buffer);
doPutValue(key, val);
break;
}
case BOOLEAN: {
val = BooleanValue.of(buffer.readBoolean());
doPutValue(key, val);
break;
}
case BYTE: {
val = ByteValue.valueOf(buffer.readByte());
doPutValue(key, val);
break;
}
case BYTES: {
val = new BytesValue(buffer);
doPutValue(key, val);
break;
}
case SHORT: {
val = new ShortValue(buffer);
doPutValue(key, val);
break;
}
case INT: {
val = new IntValue(buffer);
doPutValue(key, val);
break;
}
case LONG: {
val = new LongValue(buffer);
doPutValue(key, val);
break;
}
case FLOAT: {
val = new FloatValue(buffer);
doPutValue(key, val);
break;
}
case DOUBLE: {
val = new DoubleValue(buffer);
doPutValue(key, val);
break;
}
case STRING: {
val = StringValue.readStringValue(buffer, keyValuePools == null ? null : keyValuePools.getPropertyValuesPool());
doPutValue(key, val);
break;
}
default: {
throw ActiveMQUtilBundle.BUNDLE.invalidType(type);
}
}
}
}
}
public void decode(final ByteBuf buffer) {
decode(buffer, null);
}
public synchronized void encode(final ByteBuf buffer) {
if (properties == null || size == 0) {
buffer.writeByte(DataConstants.NULL);
} else {
buffer.writeByte(DataConstants.NOT_NULL);
buffer.writeInt(properties.size());
//uses internal iteration to allow inlining/loop unrolling
properties.forEach((key, value) -> {
final byte[] data = key.getData();
buffer.writeInt(data.length);
buffer.writeBytes(data);
value.write(buffer);
});
}
}
public synchronized int getEncodeSize() {
if (properties == null || size == 0) {
return DataConstants.SIZE_BYTE;
} else {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + size;
}
}
public synchronized void clear() {
if (properties != null) {
properties.clear();
}
size = 0;
}
@Override
public synchronized String toString() {
StringBuilder sb = new StringBuilder("TypedProperties[");
if (properties != null) {
Iterator<Entry<SimpleString, PropertyValue>> iter = properties.entrySet().iterator();
while (iter.hasNext()) {
Entry<SimpleString, PropertyValue> iterItem = iter.next();
sb.append(iterItem.getKey() + "=");
// it seems weird but it's right!!
// The first getValue is from the EntrySet
// The second is to convert the PropertyValue into the actual value
Object theValue = iterItem.getValue().getValue();
if (theValue == null) {
sb.append("NULL-value");
} else if (theValue instanceof byte[]) {
sb.append("[" + ByteUtil.maxString(ByteUtil.bytesToHex((byte[]) theValue, 2), 150) + ")");
if (iterItem.getKey().toString().startsWith("_AMQ_ROUTE_TO")) {
sb.append(",bytesAsLongs(");
try {
ByteBuffer buff = ByteBuffer.wrap((byte[]) theValue);
while (buff.hasRemaining()) {
long bindingID = buff.getLong();
sb.append(bindingID);
if (buff.hasRemaining()) {
sb.append(",");
}
}
} catch (Throwable e) {
sb.append("error-converting-longs=" + e.getMessage());
}
sb.append("]");
}
} else {
sb.append(theValue.toString());
}
if (iter.hasNext()) {
sb.append(",");
}
}
}
return sb.append("]").toString();
}
// Private ------------------------------------------------------------------------------------
private synchronized void doPutValue(final SimpleString key, final PropertyValue value) {
if (!internalProperties && internalPropertyPredicate != null && internalPropertyPredicate.test(key)) {
internalProperties = true;
}
if (properties == null) {
properties = new HashMap<>();
}
PropertyValue oldValue = properties.put(key, value);
if (oldValue != null) {
size += value.encodeSize() - oldValue.encodeSize();
} else {
size += SimpleString.sizeofString(key) + value.encodeSize();
}
}
private synchronized Object doRemoveProperty(final SimpleString key) {
if (properties == null) {
return null;
}
PropertyValue val = properties.remove(key);
if (val == null) {
return null;
} else {
size -= SimpleString.sizeofString(key) + val.encodeSize();
return val.getValue();
}
}
private synchronized Object doGetProperty(final SimpleString key) {
if (properties == null) {
return null;
}
PropertyValue val = properties.get(key);
if (val == null) {
return null;
} else {
return val.getValue();
}
}
// Inner classes ------------------------------------------------------------------------------
private abstract static class PropertyValue {
abstract Object getValue();
abstract void write(ByteBuf buffer);
abstract int encodeSize();
@Override
public String toString() {
return "" + getValue();
}
}
private static final class NullValue extends PropertyValue {
private static final NullValue INSTANCE = new NullValue();
private NullValue() {
}
@Override
public Object getValue() {
return null;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.NULL);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE;
}
}
private static final class BooleanValue extends PropertyValue {
private static final int ENCODE_SIZE = DataConstants.SIZE_BYTE + DataConstants.SIZE_BOOLEAN;
private static final BooleanValue TRUE = new BooleanValue(true);
private static final BooleanValue FALSE = new BooleanValue(false);
private final boolean val;
private final Boolean objVal;
private BooleanValue(final boolean val) {
this.val = val;
this.objVal = val;
}
private static BooleanValue of(final boolean val) {
if (val) {
return TRUE;
} else {
return FALSE;
}
}
@Override
public Object getValue() {
return objVal;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.BOOLEAN);
buffer.writeBoolean(val);
}
@Override
public int encodeSize() {
return ENCODE_SIZE;
}
}
private static final class ByteValue extends PropertyValue {
private static final int ENCODE_SIZE = DataConstants.SIZE_BYTE + DataConstants.SIZE_BYTE;
//LAZY CACHE that uses a benign race condition to avoid too many allocations of ByteValue if contended and to allocate upfront unneeded instances.
//the Java spec doesn't allow tearing while reading/writing from arrays of references
private static final ByteValue[] VALUES = new ByteValue[-(-128) + 127 + 1];
private static ByteValue valueOf(byte b) {
final int offset = 128;
final int index = (int) b + offset;
ByteValue value = VALUES[index];
if (value == null) {
return VALUES[index] = new ByteValue(b);
} else {
return value;
}
}
private final byte val;
private final Byte objectVal;
private ByteValue(final byte val) {
this.val = val;
this.objectVal = val;
}
@Override
public Object getValue() {
return objectVal;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.BYTE);
buffer.writeByte(val);
}
@Override
public int encodeSize() {
return ENCODE_SIZE;
}
}
private static final class BytesValue extends PropertyValue {
final byte[] val;
private BytesValue(final byte[] val) {
this.val = val;
}
private BytesValue(final ByteBuf buffer) {
int len = buffer.readInt();
val = new byte[len];
buffer.readBytes(val);
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.BYTES);
buffer.writeInt(val.length);
buffer.writeBytes(val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + val.length;
}
}
private static final class ShortValue extends PropertyValue {
final short val;
private ShortValue(final short val) {
this.val = val;
}
private ShortValue(final ByteBuf buffer) {
val = buffer.readShort();
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.SHORT);
buffer.writeShort(val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_SHORT;
}
}
private static final class IntValue extends PropertyValue {
final int val;
private IntValue(final int val) {
this.val = val;
}
private IntValue(final ByteBuf buffer) {
val = buffer.readInt();
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.INT);
buffer.writeInt(val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT;
}
}
private static final class LongValue extends PropertyValue {
final long val;
private LongValue(final long val) {
this.val = val;
}
private LongValue(final ByteBuf buffer) {
val = buffer.readLong();
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.LONG);
buffer.writeLong(val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_LONG;
}
}
private static final class FloatValue extends PropertyValue {
final float val;
private FloatValue(final float val) {
this.val = val;
}
private FloatValue(final ByteBuf buffer) {
val = Float.intBitsToFloat(buffer.readInt());
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.FLOAT);
buffer.writeInt(Float.floatToIntBits(val));
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_FLOAT;
}
}
private static final class DoubleValue extends PropertyValue {
final double val;
private DoubleValue(final double val) {
this.val = val;
}
private DoubleValue(final ByteBuf buffer) {
val = Double.longBitsToDouble(buffer.readLong());
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.DOUBLE);
buffer.writeLong(Double.doubleToLongBits(val));
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_DOUBLE;
}
}
private static final class CharValue extends PropertyValue {
final char val;
private CharValue(final char val) {
this.val = val;
}
private CharValue(final ByteBuf buffer) {
val = (char) buffer.readShort();
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.CHAR);
buffer.writeShort((short) val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + DataConstants.SIZE_CHAR;
}
}
public static final class StringValue extends PropertyValue {
final SimpleString val;
private StringValue(final SimpleString val) {
this.val = val;
}
static StringValue readStringValue(final ByteBuf byteBuf, ByteBufStringValuePool pool) {
if (pool == null) {
return new StringValue(SimpleString.readSimpleString(byteBuf));
} else {
return pool.getOrCreate(byteBuf);
}
}
@Override
public Object getValue() {
return val;
}
@Override
public void write(final ByteBuf buffer) {
buffer.writeByte(DataConstants.STRING);
SimpleString.writeSimpleString(buffer, val);
}
@Override
public int encodeSize() {
return DataConstants.SIZE_BYTE + SimpleString.sizeofString(val);
}
public static final class ByteBufStringValuePool extends AbstractByteBufPool<StringValue> {
public static final int DEFAULT_MAX_LENGTH = 36;
private final int maxLength;
public ByteBufStringValuePool() {
this.maxLength = DEFAULT_MAX_LENGTH;
}
public ByteBufStringValuePool(final int capacity) {
this(capacity, DEFAULT_MAX_LENGTH);
}
public ByteBufStringValuePool(final int capacity, final int maxCharsLength) {
super(capacity);
this.maxLength = maxCharsLength;
}
@Override
protected boolean isEqual(final StringValue entry, final ByteBuf byteBuf, final int offset, final int length) {
if (entry == null || entry.val == null) {
return false;
}
return entry.val.equals(byteBuf, offset, length);
}
@Override
protected boolean canPool(final ByteBuf byteBuf, final int length) {
assert length % 2 == 0 : "length must be a multiple of 2";
final int expectedStringLength = length >> 1;
return expectedStringLength <= maxLength;
}
@Override
protected StringValue create(final ByteBuf byteBuf, final int length) {
return new StringValue(SimpleString.readSimpleString(byteBuf, length));
}
}
}
public static class TypedPropertiesDecoderPools {
private SimpleString.ByteBufSimpleStringPool propertyKeysPool;
private TypedProperties.StringValue.ByteBufStringValuePool propertyValuesPool;
public TypedPropertiesDecoderPools() {
this.propertyKeysPool = new SimpleString.ByteBufSimpleStringPool();
this.propertyValuesPool = new TypedProperties.StringValue.ByteBufStringValuePool();
}
public TypedPropertiesDecoderPools(int keyPoolCapacity, int valuePoolCapacity) {
this.propertyKeysPool = new SimpleString.ByteBufSimpleStringPool(keyPoolCapacity);
this.propertyValuesPool = new TypedProperties.StringValue.ByteBufStringValuePool(valuePoolCapacity);
}
public SimpleString.ByteBufSimpleStringPool getPropertyKeysPool() {
return propertyKeysPool;
}
public TypedProperties.StringValue.ByteBufStringValuePool getPropertyValuesPool() {
return propertyValuesPool;
}
}
public static class TypedPropertiesStringSimpleStringPools {
private SimpleString.StringSimpleStringPool propertyKeysPool;
private SimpleString.StringSimpleStringPool propertyValuesPool;
public TypedPropertiesStringSimpleStringPools() {
this.propertyKeysPool = new SimpleString.StringSimpleStringPool();
this.propertyValuesPool = new SimpleString.StringSimpleStringPool();
}
public TypedPropertiesStringSimpleStringPools(int keyPoolCapacity, int valuePoolCapacity) {
this.propertyKeysPool = new SimpleString.StringSimpleStringPool(keyPoolCapacity);
this.propertyValuesPool = new SimpleString.StringSimpleStringPool(valuePoolCapacity);
}
public SimpleString.StringSimpleStringPool getPropertyKeysPool() {
return propertyKeysPool;
}
public SimpleString.StringSimpleStringPool getPropertyValuesPool() {
return propertyValuesPool;
}
}
public synchronized boolean isEmpty() {
if (properties == null) {
return true;
} else {
return properties.isEmpty();
}
}
public synchronized Set<String> getMapNames() {
if (properties == null) {
return Collections.emptySet();
} else {
Set<String> names = new HashSet<>(properties.size());
for (SimpleString name : properties.keySet()) {
names.add(name.toString());
}
return names;
}
}
public synchronized Map<String, Object> getMap() {
if (properties == null) {
return Collections.emptyMap();
} else {
Map<String, Object> m = new HashMap<>(properties.size());
for (Entry<SimpleString, PropertyValue> entry : properties.entrySet()) {
Object val = entry.getValue().getValue();
if (val instanceof SimpleString) {
m.put(entry.getKey().toString(), ((SimpleString) val).toString());
} else {
m.put(entry.getKey().toString(), val);
}
}
return m;
}
}
/**
* Helper for MapMessage#setObjectProperty(String, Object)
*
* @param key The SimpleString key
* @param value The Object value
* @param properties The typed properties
*/
public static void setObjectProperty(final SimpleString key, final Object value, final TypedProperties properties) {
if (value == null) {
properties.putNullValue(key);
} else if (value instanceof Boolean) {
properties.putBooleanProperty(key, (Boolean) value);
} else if (value instanceof Byte) {
properties.putByteProperty(key, (Byte) value);
} else if (value instanceof Character) {
properties.putCharProperty(key, (Character) value);
} else if (value instanceof Short) {
properties.putShortProperty(key, (Short) value);
} else if (value instanceof Integer) {
properties.putIntProperty(key, (Integer) value);
} else if (value instanceof Long) {
properties.putLongProperty(key, (Long) value);
} else if (value instanceof Float) {
properties.putFloatProperty(key, (Float) value);
} else if (value instanceof Double) {
properties.putDoubleProperty(key, (Double) value);
} else if (value instanceof String) {
properties.putSimpleStringProperty(key, new SimpleString((String) value));
} else if (value instanceof SimpleString) {
properties.putSimpleStringProperty(key, (SimpleString) value);
} else if (value instanceof byte[]) {
properties.putBytesProperty(key, (byte[]) value);
} else {
throw new ActiveMQPropertyConversionException(value.getClass() + " is not a valid property type");
}
}
}