/*
* Copyright © 2015 Cask Data, Inc.
*
* 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 co.cask.cdap.api.dataset.lib;
import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.api.data.format.StructuredRecord;
import co.cask.cdap.api.data.schema.Schema;
import co.cask.cdap.api.dataset.DatasetProperties;
import co.cask.cdap.api.dataset.table.Put;
import co.cask.cdap.api.dataset.table.Row;
import co.cask.cdap.api.dataset.table.Table;
import co.cask.cdap.data2.dataset2.DatasetFrameworkTestUtil;
import co.cask.cdap.internal.io.ReflectionPutWriter;
import co.cask.cdap.internal.io.ReflectionRowReader;
import co.cask.cdap.internal.io.ReflectionRowRecordReader;
import co.cask.cdap.internal.io.ReflectionSchemaGenerator;
import co.cask.cdap.proto.Id;
import co.cask.tephra.TransactionAware;
import co.cask.tephra.TransactionExecutor;
import com.google.common.base.Objects;
import com.google.common.reflect.TypeToken;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
*
*/
public class ReflectionTableTest {
@ClassRule
public static DatasetFrameworkTestUtil dsFrameworkUtil = new DatasetFrameworkTestUtil();
private static final Id.DatasetInstance users =
Id.DatasetInstance.from(DatasetFrameworkTestUtil.NAMESPACE_ID, "users");
private static final User SAMUEL = new User(
"Samuel L.", "Jackson",
123,
1234567890000L,
50000000.02f,
Double.MAX_VALUE,
new byte[] { 0, 1, 2 });
private static final User SAMUEL_NO_FIRST = new User(
null, "Jackson",
123,
1234567890123L,
50000000.02f,
Double.MAX_VALUE,
new byte[] { 0, 4, 9 });
private static final User SAMUEL_NO_ID = new User(
"Samuel L.", "Jackson",
null,
1234567890000L,
50000000.02f,
Double.MAX_VALUE,
new byte[] { 0, 1, 2 });
private static final User SAMUEL_NO_TS = new User(
"Samuel L.", "Jackson",
123,
null,
50000000.02f,
Double.MAX_VALUE,
new byte[] { 0, 1, 2 });
private static final User SAMUEL_NO_SALARY = new User(
"Samuel L.", "Jackson",
123,
1234567890123L,
null,
Double.MAX_VALUE,
new byte[] { 0, 4, 9 });
private static final User SAMUEL_NO_PURCHASE = new User(
"Samuel L.", "Jackson",
123,
1234567890456L,
null,
Double.MAX_VALUE,
new byte[] { 0, 3, 7 });
private static final User SAMUEL_NO_BLOB = new User(
"Samuel L.", "Jackson",
123,
1234567890000L,
50000000.02f,
Double.MAX_VALUE,
null);
public static class User {
private String firstName;
private String lastName;
private Integer id;
private Long timestamp;
private Float salary;
private Double lastPurchase;
private byte[] blob;
public User(String firstName, String lastName, Integer id, Long timestamp,
Float salary, Double lastPurchase, byte[] blob) {
this.firstName = firstName;
this.lastName = lastName;
this.id = id;
this.timestamp = timestamp;
this.salary = salary;
this.lastPurchase = lastPurchase;
this.blob = blob;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User that = (User) o;
return Objects.equal(firstName, that.firstName) &&
Objects.equal(lastName, that.lastName) &&
Objects.equal(id, that.id) &&
Objects.equal(timestamp, that.timestamp) &&
Objects.equal(salary, that.salary) &&
Objects.equal(lastPurchase, that.lastPurchase) &&
Arrays.equals(blob, that.blob);
}
@Override
public int hashCode() {
return Objects.hashCode(firstName, lastName, id, timestamp, salary, lastPurchase, blob);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("firstName", firstName)
.add("lastName", lastName)
.add("id", id)
.add("timestamp", timestamp)
.add("salary", salary)
.add("lastPurchase", lastPurchase)
.add("blob", blob)
.toString();
}
}
public static class User2 {
private String firstName;
private Long id;
private Double salary;
private Double lastPurchase;
private ByteBuffer blob;
private Double newField;
private User2(String firstName, Long id, Double salary, Double lastPurchase, ByteBuffer blob) {
this.firstName = firstName;
this.id = id;
this.salary = salary;
this.lastPurchase = lastPurchase;
this.blob = blob;
this.newField = null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User2)) {
return false;
}
User2 that = (User2) o;
return Objects.equal(firstName, that.firstName) &&
Objects.equal(id, that.id) &&
Objects.equal(salary, that.salary) &&
Objects.equal(lastPurchase, that.lastPurchase) &&
Objects.equal(blob, that.blob) &&
Objects.equal(newField, that.newField);
}
@Override
public int hashCode() {
return Objects.hashCode(firstName, id, salary, lastPurchase, blob, newField);
}
}
@Test
public void testPutAndGet() throws Exception {
dsFrameworkUtil.createInstance("table", users, DatasetProperties.builder().build());
try {
final Table usersTable = dsFrameworkUtil.getInstance(users);
final byte[] rowKey = Bytes.toBytes(123);
final Schema schema = new ReflectionSchemaGenerator().generate(User.class);
assertGetAndPut(usersTable, rowKey, SAMUEL, schema);
} finally {
dsFrameworkUtil.deleteInstance(users);
}
}
@Test
public void testNullFields() throws Exception {
dsFrameworkUtil.createInstance("table", users, DatasetProperties.builder().build());
try {
final Table usersTable = dsFrameworkUtil.getInstance(users);
final byte[] rowKey = Bytes.toBytes(123);
final Schema schema = new ReflectionSchemaGenerator().generate(User.class);
assertGetAndPut(usersTable, rowKey, SAMUEL, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_TS, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_ID, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_FIRST, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_SALARY, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_PURCHASE, schema);
assertGetAndPut(usersTable, rowKey, SAMUEL_NO_BLOB, schema);
} finally {
dsFrameworkUtil.deleteInstance(users);
}
}
@Test
public void testTypeProjection() throws Exception {
dsFrameworkUtil.createInstance("table", users, DatasetProperties.builder().build());
try {
final Table usersTable = dsFrameworkUtil.getInstance(users);
final byte[] rowKey = Bytes.toBytes(123);
final User2 projected = new User2("Samuel L.", 123L, ((Float) 50000000.02f).doubleValue(), Double.MAX_VALUE,
ByteBuffer.wrap(new byte[]{0, 1, 2}));
final Schema fullSchema = new ReflectionSchemaGenerator().generate(User.class);
final Schema projSchema = new ReflectionSchemaGenerator().generate(User2.class);
// TableDataset is not accessible here, but we know that's the underlying implementation...
TransactionExecutor tx = dsFrameworkUtil.newTransactionExecutor((TransactionAware) usersTable);
tx.execute(new TransactionExecutor.Subroutine() {
@Override
public void apply() throws Exception {
Put put = new Put(rowKey);
ReflectionPutWriter<User> putWriter = new ReflectionPutWriter<>(fullSchema);
putWriter.write(SAMUEL, put);
usersTable.put(put);
Row row = usersTable.get(rowKey);
ReflectionRowReader<User2> rowReader = new ReflectionRowReader<>(projSchema, TypeToken.of(User2.class));
User2 actual = rowReader.read(row, fullSchema);
Assert.assertEquals(projected, actual);
}
});
} finally {
dsFrameworkUtil.deleteInstance(users);
}
}
@Test
public void testStructuredRecordRepresentation() throws Exception {
dsFrameworkUtil.createInstance("table", users, DatasetProperties.builder().build());
try {
final Table usersTable = dsFrameworkUtil.getInstance(users);
final byte[] rowKey = Bytes.toBytes(123);
final Schema schema = new ReflectionSchemaGenerator().generate(User.class);
// TableDataset is not accessible here, but we know that's the underlying implementation...
TransactionExecutor tx = dsFrameworkUtil.newTransactionExecutor((TransactionAware) usersTable);
tx.execute(new TransactionExecutor.Subroutine() {
@Override
public void apply() throws Exception {
Put put = new Put(rowKey);
ReflectionPutWriter<User> putWriter = new ReflectionPutWriter<>(schema);
putWriter.write(SAMUEL, put);
usersTable.put(put);
Row row = usersTable.get(rowKey);
ReflectionRowRecordReader rowReader = new ReflectionRowRecordReader(schema, null);
StructuredRecord actual = rowReader.read(row, schema);
assertRecordEqualsUser(SAMUEL, actual);
}
});
} finally {
dsFrameworkUtil.deleteInstance(users);
}
}
@Test
public void testStructuredRecordProjection() throws Exception {
dsFrameworkUtil.createInstance("table", users, DatasetProperties.builder().build());
try {
final Table usersTable = dsFrameworkUtil.getInstance(users);
final byte[] rowKey = Bytes.toBytes(123);
final User2 projected = new User2("Samuel L.", 123L, ((Float) 50000000.02f).doubleValue(), Double.MAX_VALUE,
ByteBuffer.wrap(new byte[]{0, 1, 2}));
final Schema fullSchema = new ReflectionSchemaGenerator().generate(User.class);
final Schema projSchema = new ReflectionSchemaGenerator().generate(User2.class);
// TableDataset is not accessible here, but we know that's the underlying implementation...
TransactionExecutor tx = dsFrameworkUtil.newTransactionExecutor((TransactionAware) usersTable);
tx.execute(new TransactionExecutor.Subroutine() {
@Override
public void apply() throws Exception {
Put put = new Put(rowKey);
ReflectionPutWriter<User> putWriter = new ReflectionPutWriter<>(fullSchema);
putWriter.write(SAMUEL, put);
usersTable.put(put);
Row row = usersTable.get(rowKey);
ReflectionRowRecordReader rowReader = new ReflectionRowRecordReader(projSchema, null);
StructuredRecord actual = rowReader.read(row, fullSchema);
assertRecordEqualsUser(projected, actual);
}
});
} finally {
dsFrameworkUtil.deleteInstance(users);
}
}
private void assertGetAndPut(final Table table, final byte[] rowKey, final User obj,
final Schema schema) throws Exception {
// TableDataset is not accessible here, but we know that's the underlying implementation...
TransactionExecutor tx = dsFrameworkUtil.newTransactionExecutor((TransactionAware) table);
tx.execute(new TransactionExecutor.Subroutine() {
@Override
public void apply() throws Exception {
Put put = new Put(rowKey);
ReflectionPutWriter<User> putWriter = new ReflectionPutWriter<>(schema);
putWriter.write(obj, put);
table.put(put);
Row row = table.get(rowKey);
ReflectionRowReader<User> rowReader = new ReflectionRowReader<>(schema, TypeToken.of(User.class));
User actual = rowReader.read(row, schema);
Assert.assertEquals(obj, actual);
}
});
}
private void assertRecordEqualsUser(User expected, StructuredRecord actual) {
Assert.assertEquals(expected.firstName, actual.get("firstName"));
Assert.assertEquals(expected.lastName, actual.get("lastName"));
Assert.assertEquals(expected.id, actual.get("id"));
Assert.assertEquals(expected.timestamp, actual.get("timestamp"));
Assert.assertEquals(expected.salary, actual.get("salary"));
Assert.assertEquals(expected.lastPurchase, actual.get("lastPurchase"));
Assert.assertEquals(ByteBuffer.wrap(expected.blob), actual.get("blob"));
}
private void assertRecordEqualsUser(User2 expected, StructuredRecord actual) {
Assert.assertEquals(expected.firstName, actual.get("firstName"));
Assert.assertEquals(expected.id, actual.get("id"));
Assert.assertEquals(expected.salary, actual.get("salary"));
Assert.assertEquals(expected.lastPurchase, actual.get("lastPurchase"));
Assert.assertEquals(expected.blob, actual.get("blob"));
Assert.assertEquals(expected.newField, actual.get("newField"));
}
}