/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.client;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.teiid.core.types.ArrayImpl;
import org.teiid.core.types.BinaryType;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.types.GeometryType;
import org.teiid.core.util.TimestampWithTimezone;
import org.teiid.query.unittest.TimestampUtil;
/**
* @since 4.2
*/
@SuppressWarnings("nls")
public class TestBatchSerializer {
private static List<List<Object>> helpTestSerialization(String[] types, List<?>[] batch, byte version) throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteStream);
List<List<?>> batchList = Arrays.asList(batch);
BatchSerializer.writeBatch(out, types, batchList, version);
out.flush();
byte[] bytes = byteStream.toByteArray();
ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(bytesIn);
List<List<Object>> newBatch = BatchSerializer.readBatch(in, types);
out.close();
in.close();
assertTrue(batchList.equals(newBatch));
return newBatch;
}
private static final String[] sampleBatchTypes = {DataTypeManager.DefaultDataTypes.BIG_DECIMAL,
DataTypeManager.DefaultDataTypes.BIG_INTEGER,
DataTypeManager.DefaultDataTypes.BOOLEAN,
DataTypeManager.DefaultDataTypes.BYTE,
DataTypeManager.DefaultDataTypes.CHAR,
DataTypeManager.DefaultDataTypes.DATE,
DataTypeManager.DefaultDataTypes.DOUBLE,
DataTypeManager.DefaultDataTypes.FLOAT,
DataTypeManager.DefaultDataTypes.INTEGER,
DataTypeManager.DefaultDataTypes.LONG,
DataTypeManager.DefaultDataTypes.SHORT,
DataTypeManager.DefaultDataTypes.STRING,
DataTypeManager.DefaultDataTypes.TIME,
DataTypeManager.DefaultDataTypes.TIMESTAMP,
DataTypeManager.DefaultDataTypes.OBJECT,
DataTypeManager.DefaultDataTypes.VARBINARY,
};
private static String sampleString(int length) {
char[] chars = new char[length];
for (int i = 0; i < length; i++) {
chars[i] = (char)i;
}
return new String(chars);
}
private static List<?>[] sampleBatchWithNulls(int rows) {
List<?>[] batch = new List[rows];
for (int i = 0; i < rows; i++) {
java.util.Date d = new java.util.Date();
int mod = i%16;
Object[] data = { (mod == 0) ? null : new BigDecimal("" + i), //$NON-NLS-1$
(mod == 1) ? null : new BigInteger(Integer.toString(i)),
(mod == 2) ? null : ((i%2 == 0) ? Boolean.FALSE: Boolean.TRUE),
(mod == 3) ? null : new Byte((byte)i),
(mod == 4) ? null : new Character((char)i),
(mod == 5) ? null : TimestampWithTimezone.createDate(d),
(mod == 6) ? null : new Double(i),
(mod == 7) ? null : new Float(i),
(mod == 8) ? null : new Integer(i),
(mod == 9) ? null : new Long(i),
(mod == 10) ? null : new Short((short)i),
(mod == 11) ? null : sampleString(i),
(mod == 12) ? null : TimestampWithTimezone.createTime(d),
(mod == 13) ? null : TimestampWithTimezone.createTimestamp(d),
(mod == 14) ? null : TimestampWithTimezone.createTimestamp(d),
(mod == 15) ? null : new BinaryType(new byte[] {(byte)i}),
};
batch[i] = Arrays.asList(data);
}
return batch;
}
@Test public void testSerializeBasicTypes() throws Exception {
// The number 8 is important here because boolean isNull information is packed into bytes,
// so we want to make sure the boundary cases are handled correctly
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(1), BatchSerializer.CURRENT_VERSION); // Less than 8 rows
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(8), BatchSerializer.CURRENT_VERSION); // Exactly 8 rows
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(17), BatchSerializer.CURRENT_VERSION); // More than 8 rows, but not a multiple of 8
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(120), BatchSerializer.CURRENT_VERSION); // A multiple of 8 rows
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(833), BatchSerializer.CURRENT_VERSION); // A bunch of rows. This should also test large strings
helpTestSerialization(sampleBatchTypes, sampleBatchWithNulls(4096), BatchSerializer.CURRENT_VERSION); // A bunch of rows. This should also test large strings
}
@Test public void testSerializeLargeStrings() throws Exception {
List<?> row = Arrays.asList(new Object[] {sampleString(66666)});
helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.STRING}, new List[] {row}, BatchSerializer.CURRENT_VERSION);
}
@Test public void testSerializeNoData() throws Exception {
helpTestSerialization(sampleBatchTypes, new List[0], BatchSerializer.CURRENT_VERSION);
}
@Test public void testSerializeDatatypeMismatch() throws Exception {
try {
helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.DOUBLE}, new List[] {Arrays.asList(new Object[] {"Hello!"})}, BatchSerializer.CURRENT_VERSION); //$NON-NLS-1$
} catch (RuntimeException e) {
assertEquals("TEIID20001 The modeled datatype double for column 0 doesn't match the runtime type \"java.lang.String\". Please ensure that the column's modeled datatype matches the expected data.", e.getMessage()); //$NON-NLS-1$
}
}
@Test(expected=IOException.class) public void testOutOfRangeDate() throws Exception {
helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.DATE}, new List[] {Arrays.asList(TimestampUtil.createDate(-2, 0, 1))}, (byte)1);
}
@Test public void testStringArray() throws IOException, ClassNotFoundException {
helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.LONG, "string[]"}, new List[] {Arrays.asList(1l, new ArrayImpl(new String[] {"Silly String", "Silly String"}))}, BatchSerializer.CURRENT_VERSION);
}
@Test public void testGeometry() throws IOException, ClassNotFoundException {
GeometryType geometryType = new GeometryType(new byte[0]);
geometryType.setReferenceStreamId(null);
geometryType.setSrid(10000);
Object val = helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.GEOMETRY}, new List[] {Arrays.asList(geometryType)}, BatchSerializer.CURRENT_VERSION).get(0).get(0);
assertTrue(val instanceof GeometryType);
assertEquals(10000, ((GeometryType)val).getSrid());
helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.GEOMETRY}, new List[] {Arrays.asList(geometryType)}, (byte)0); //object serialization - should fail on the client side
val = helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.GEOMETRY}, new List[] {Arrays.asList(geometryType)}, (byte)1); //blob serialization
assertFalse(val instanceof GeometryType);
val = helpTestSerialization(new String[] {DataTypeManager.DefaultDataTypes.OBJECT}, new List[] {Arrays.asList(geometryType)}, (byte)1); //blob serialization
assertFalse(val instanceof GeometryType);
}
}