/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.transport;
import java.nio.ByteBuffer;
import java.util.*;
import io.netty.buffer.Unpooled;
import io.netty.buffer.ByteBuf;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.transport.Event.TopologyChange;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.StatusChange;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import static org.junit.Assert.assertEquals;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
/**
* Serialization/deserialization tests for protocol objects and messages.
*/
public class SerDeserTest
{
@BeforeClass
public static void setupDD()
{
// required for making the paging state
DatabaseDescriptor.daemonInitialization();
}
@Test
public void collectionSerDeserTest() throws Exception
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
collectionSerDeserTest(version);
}
public void collectionSerDeserTest(ProtocolVersion version) throws Exception
{
// Lists
ListType<?> lt = ListType.getInstance(Int32Type.instance, true);
List<Integer> l = Arrays.asList(2, 6, 1, 9);
List<ByteBuffer> lb = new ArrayList<>(l.size());
for (Integer i : l)
lb.add(Int32Type.instance.decompose(i));
assertEquals(l, lt.getSerializer().deserializeForNativeProtocol(CollectionSerializer.pack(lb, lb.size(), version), version));
// Sets
SetType<?> st = SetType.getInstance(UTF8Type.instance, true);
Set<String> s = new LinkedHashSet<>();
s.addAll(Arrays.asList("bar", "foo", "zee"));
List<ByteBuffer> sb = new ArrayList<>(s.size());
for (String t : s)
sb.add(UTF8Type.instance.decompose(t));
assertEquals(s, st.getSerializer().deserializeForNativeProtocol(CollectionSerializer.pack(sb, sb.size(), version), version));
// Maps
MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance, true);
Map<String, Long> m = new LinkedHashMap<>();
m.put("bar", 12L);
m.put("foo", 42L);
m.put("zee", 14L);
List<ByteBuffer> mb = new ArrayList<>(m.size() * 2);
for (Map.Entry<String, Long> entry : m.entrySet())
{
mb.add(UTF8Type.instance.decompose(entry.getKey()));
mb.add(LongType.instance.decompose(entry.getValue()));
}
assertEquals(m, mt.getSerializer().deserializeForNativeProtocol(CollectionSerializer.pack(mb, m.size(), version), version));
}
@Test
public void eventSerDeserTest() throws Exception
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
eventSerDeserTest(version);
}
public void eventSerDeserTest(ProtocolVersion version) throws Exception
{
List<Event> events = new ArrayList<>();
events.add(TopologyChange.newNode(FBUtilities.getBroadcastAddress(), 42));
events.add(TopologyChange.removedNode(FBUtilities.getBroadcastAddress(), 42));
events.add(TopologyChange.movedNode(FBUtilities.getBroadcastAddress(), 42));
events.add(StatusChange.nodeUp(FBUtilities.getBroadcastAddress(), 42));
events.add(StatusChange.nodeDown(FBUtilities.getBroadcastAddress(), 42));
events.add(new SchemaChange(SchemaChange.Change.CREATED, "ks"));
events.add(new SchemaChange(SchemaChange.Change.UPDATED, "ks"));
events.add(new SchemaChange(SchemaChange.Change.DROPPED, "ks"));
events.add(new SchemaChange(SchemaChange.Change.CREATED, SchemaChange.Target.TABLE, "ks", "table"));
events.add(new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.TABLE, "ks", "table"));
events.add(new SchemaChange(SchemaChange.Change.DROPPED, SchemaChange.Target.TABLE, "ks", "table"));
if (version.isGreaterOrEqualTo(ProtocolVersion.V3))
{
events.add(new SchemaChange(SchemaChange.Change.CREATED, SchemaChange.Target.TYPE, "ks", "type"));
events.add(new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.TYPE, "ks", "type"));
events.add(new SchemaChange(SchemaChange.Change.DROPPED, SchemaChange.Target.TYPE, "ks", "type"));
}
if (version.isGreaterOrEqualTo(ProtocolVersion.V4))
{
List<String> moreTypes = Arrays.asList("text", "bigint");
events.add(new SchemaChange(SchemaChange.Change.CREATED, SchemaChange.Target.FUNCTION, "ks", "func", Collections.<String>emptyList()));
events.add(new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.FUNCTION, "ks", "func", moreTypes));
events.add(new SchemaChange(SchemaChange.Change.DROPPED, SchemaChange.Target.FUNCTION, "ks", "func", moreTypes));
events.add(new SchemaChange(SchemaChange.Change.CREATED, SchemaChange.Target.AGGREGATE, "ks", "aggr", Collections.<String>emptyList()));
events.add(new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.AGGREGATE, "ks", "aggr", moreTypes));
events.add(new SchemaChange(SchemaChange.Change.DROPPED, SchemaChange.Target.AGGREGATE, "ks", "aggr", moreTypes));
}
for (Event ev : events)
{
ByteBuf buf = Unpooled.buffer(ev.serializedSize(version));
ev.serialize(buf, version);
assertEquals(ev, Event.deserialize(buf, version));
}
}
private static ByteBuffer bb(String str)
{
return UTF8Type.instance.decompose(str);
}
private static FieldIdentifier field(String field)
{
return FieldIdentifier.forQuoted(field);
}
private static ColumnIdentifier ci(String name)
{
return new ColumnIdentifier(name, false);
}
private static Constants.Literal lit(long v)
{
return Constants.Literal.integer(String.valueOf(v));
}
private static Constants.Literal lit(String v)
{
return Constants.Literal.string(v);
}
private static ColumnSpecification columnSpec(String name, AbstractType<?> type)
{
return new ColumnSpecification("ks", "cf", ci(name), type);
}
@Test
public void udtSerDeserTest() throws Exception
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
udtSerDeserTest(version);
}
public void udtSerDeserTest(ProtocolVersion version) throws Exception
{
ListType<?> lt = ListType.getInstance(Int32Type.instance, true);
SetType<?> st = SetType.getInstance(UTF8Type.instance, true);
MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance, true);
UserType udt = new UserType("ks",
bb("myType"),
Arrays.asList(field("f1"), field("f2"), field("f3"), field("f4")),
Arrays.asList(LongType.instance, lt, st, mt),
true);
Map<FieldIdentifier, Term.Raw> value = new HashMap<>();
value.put(field("f1"), lit(42));
value.put(field("f2"), new Lists.Literal(Arrays.<Term.Raw>asList(lit(3), lit(1))));
value.put(field("f3"), new Sets.Literal(Arrays.<Term.Raw>asList(lit("foo"), lit("bar"))));
value.put(field("f4"), new Maps.Literal(Arrays.<Pair<Term.Raw, Term.Raw>>asList(
Pair.<Term.Raw, Term.Raw>create(lit("foo"), lit(24)),
Pair.<Term.Raw, Term.Raw>create(lit("bar"), lit(12)))));
UserTypes.Literal u = new UserTypes.Literal(value);
Term t = u.prepare("ks", columnSpec("myValue", udt));
QueryOptions options = QueryOptions.DEFAULT;
ByteBuffer serialized = t.bindAndGet(options);
ByteBuffer[] fields = udt.split(serialized);
assertEquals(4, fields.length);
assertEquals(bytes(42L), fields[0]);
// Note that no matter what the protocol version has been used in bindAndGet above, the collections inside
// a UDT should alway be serialized with version 3 of the protocol. Which is why we don't use 'version'
// on purpose below.
assertEquals(Arrays.asList(3, 1), lt.getSerializer().deserializeForNativeProtocol(fields[1], ProtocolVersion.V3));
LinkedHashSet<String> s = new LinkedHashSet<>();
s.addAll(Arrays.asList("bar", "foo"));
assertEquals(s, st.getSerializer().deserializeForNativeProtocol(fields[2], ProtocolVersion.V3));
LinkedHashMap<String, Long> m = new LinkedHashMap<>();
m.put("bar", 12L);
m.put("foo", 24L);
assertEquals(m, mt.getSerializer().deserializeForNativeProtocol(fields[3], ProtocolVersion.V3));
}
@Test
public void preparedMetadataSerializationTest()
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
preparedMetadataSerializationTest(version);
}
private void preparedMetadataSerializationTest(ProtocolVersion version)
{
List<ColumnSpecification> columnNames = new ArrayList<>();
for (int i = 0; i < 3; i++)
columnNames.add(new ColumnSpecification("ks", "cf", new ColumnIdentifier("col" + i, false), Int32Type.instance));
if (version == ProtocolVersion.V3)
{
// v3 encoding doesn't include partition key bind indexes
ResultSet.PreparedMetadata meta = new ResultSet.PreparedMetadata(columnNames, new short[]{ 2, 1 });
ByteBuf buf = Unpooled.buffer(ResultSet.PreparedMetadata.codec.encodedSize(meta, version));
ResultSet.PreparedMetadata.codec.encode(meta, buf, version);
ResultSet.PreparedMetadata decodedMeta = ResultSet.PreparedMetadata.codec.decode(buf, version);
assertNotSame(meta, decodedMeta);
// however, if there are no partition key indexes, they should be the same
ResultSet.PreparedMetadata metaWithoutIndexes = new ResultSet.PreparedMetadata(columnNames, null);
buf = Unpooled.buffer(metaWithoutIndexes.codec.encodedSize(metaWithoutIndexes, version));
metaWithoutIndexes.codec.encode(metaWithoutIndexes, buf, version);
ResultSet.PreparedMetadata decodedMetaWithoutIndexes = metaWithoutIndexes.codec.decode(buf, version);
assertEquals(decodedMeta, decodedMetaWithoutIndexes);
}
else
{
ResultSet.PreparedMetadata meta = new ResultSet.PreparedMetadata(columnNames, new short[]{ 2, 1 });
ByteBuf buf = Unpooled.buffer(ResultSet.PreparedMetadata.codec.encodedSize(meta, version));
ResultSet.PreparedMetadata.codec.encode(meta, buf, version);
ResultSet.PreparedMetadata decodedMeta = ResultSet.PreparedMetadata.codec.decode(buf, version);
assertEquals(meta, decodedMeta);
}
}
@Test
public void metadataSerializationTest()
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
metadataSerializationTest(version);
}
private void metadataSerializationTest(ProtocolVersion version)
{
List<ColumnSpecification> columnNames = new ArrayList<>();
for (int i = 0; i < 3; i++)
columnNames.add(new ColumnSpecification("ks", "cf", new ColumnIdentifier("col" + i, false), Int32Type.instance));
ResultSet.ResultMetadata meta = new ResultSet.ResultMetadata(columnNames);
ByteBuf buf = Unpooled.buffer(meta.codec.encodedSize(meta, version));
meta.codec.encode(meta, buf, version);
ResultSet.ResultMetadata decodedMeta = meta.codec.decode(buf, version);
assertEquals(meta, decodedMeta);
}
@Test
public void queryOptionsSerDeserTest() throws Exception
{
for (ProtocolVersion version : ProtocolVersion.SUPPORTED)
queryOptionsSerDeserTest(version);
}
private void queryOptionsSerDeserTest(ProtocolVersion version) throws Exception
{
queryOptionsSerDeserTest(version, QueryOptions.create(ConsistencyLevel.ALL,
Collections.singletonList(ByteBuffer.wrap(new byte[] { 0x00, 0x01, 0x02 })),
false,
5000,
Util.makeSomePagingState(version),
ConsistencyLevel.SERIAL,
version,
null
));
queryOptionsSerDeserTest(version, QueryOptions.create(ConsistencyLevel.LOCAL_ONE,
Arrays.asList(ByteBuffer.wrap(new byte[] { 0x00, 0x01, 0x02 }),
ByteBuffer.wrap(new byte[] { 0x03, 0x04, 0x05, 0x03, 0x04, 0x05 })),
true,
10,
Util.makeSomePagingState(version),
ConsistencyLevel.SERIAL,
version,
"some_keyspace"
));
}
private void queryOptionsSerDeserTest(ProtocolVersion version, QueryOptions options)
{
ByteBuf buf = Unpooled.buffer(QueryOptions.codec.encodedSize(options, version));
QueryOptions.codec.encode(options, buf, version);
QueryOptions decodedOptions = QueryOptions.codec.decode(buf, version);
assertNotNull(decodedOptions);
assertEquals(options.getConsistency(), decodedOptions.getConsistency());
assertEquals(options.getSerialConsistency(), decodedOptions.getSerialConsistency());
assertEquals(options.getPageSize(), decodedOptions.getPageSize());
assertEquals(options.getProtocolVersion(), decodedOptions.getProtocolVersion());
assertEquals(options.getValues(), decodedOptions.getValues());
assertEquals(options.getPagingState(), decodedOptions.getPagingState());
assertEquals(options.skipMetadata(), decodedOptions.skipMetadata());
assertEquals(options.getKeyspace(), decodedOptions.getKeyspace());
}
}