/*
* Copyright 2014, The Sporting Exchange Limited
*
* 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 com.betfair.cougar.netutil.nio.marshalling;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.betfair.cougar.netutil.nio.CougarProtocol;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.betfair.cougar.core.api.transcription.Parameter;
import com.betfair.cougar.core.api.transcription.ParameterType;
import com.betfair.cougar.core.api.transcription.TranscriptionException;
import com.betfair.cougar.marshalling.impl.to.ComplexTO;
import com.betfair.cougar.marshalling.impl.to.EnumTO;
import com.betfair.cougar.marshalling.impl.to.EnumType;
import com.betfair.cougar.marshalling.impl.to.TO;
import com.betfair.cougar.marshalling.impl.util.ByteArrayClassLoader;
import com.betfair.cougar.marshalling.impl.util.ComplexObjectCreator;
import com.betfair.cougar.marshalling.impl.util.EnumCreator;
import com.betfair.cougar.marshalling.impl.util.Pair;
import com.betfair.cougar.transport.api.protocol.CougarObjectOutput;
import com.betfair.cougar.netutil.nio.hessian.HessianObjectIOFactory;
import com.betfair.cougar.netutil.nio.hessian.HessianObjectInput;
import com.betfair.cougar.netutil.nio.hessian.HessianObjectOutput;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class SocketRMIMarshallerArgsTest {
private SocketRMIMarshaller cut;
private HessianObjectIOFactory ioFactory;
private boolean server;
public SocketRMIMarshallerArgsTest(boolean server) {
this.server = server;
}
@Parameterized.Parameters
public static Collection<Object[]> params() {
List<Object[]> ret = new ArrayList<Object[]>();
ret.add(new Object[] {true});
ret.add(new Object[] {false});
return ret;
}
@Before
public void setup() {
cut= new SocketRMIMarshaller();
ioFactory = new HessianObjectIOFactory(false);
}
@Test
/**
* dynamically create a class that has a single integer field (set to value 12) and write it, read it back using the TO class that has two
* fields (initialised to 9 and 7)
*/
public void testAdditionalField() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ComplexObjectCreator complexObjectCreator = new ComplexObjectCreator("com.betfair.cougar.marshalling.impl.to.TO");
complexObjectCreator.create(bos, new Pair[] {new Pair<String,String>(Integer.class.getName(),"i")});
ByteArrayClassLoader bcl = new ByteArrayClassLoader(complexObjectCreator.objectType, bos.toByteArray());
Class<?> clazz = Class.forName(complexObjectCreator.objectType, true, bcl);
Object o = clazz.newInstance();
Field f = clazz.getField("i");
f.set(o, 12);
Parameter[] params = new Parameter[1];
ParameterType[] parameterTypes = new ParameterType[1];
parameterTypes[0] = new ParameterType(int.class,null);
params[0] = new Parameter("TO", new ParameterType(TO.class,parameterTypes),false);
bos.reset();
CougarObjectOutput hoo = ioFactory.newCougarObjectOutput(bos, CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED);
cut.writeArgs(params, new Object[] {o}, hoo);
hoo.flush();
Parameter[] toParameters = new Parameter[1];
parameterTypes = new ParameterType[2];
parameterTypes[0] = new ParameterType(int.class,null);
parameterTypes[1] = new ParameterType(int.class,null);
toParameters[0] = new Parameter("TO", new ParameterType(TO.class,parameterTypes) ,false);
Object[] result = cut.readArgs(toParameters, ioFactory.newCougarObjectInput(new ByteArrayInputStream(bos.toByteArray()), CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED));
Assert.assertEquals(1, result.length);
Assert.assertEquals(result[0].getClass(), TO.class);
Assert.assertFalse(TO.class.equals(clazz));
Assert.assertEquals(12, ((TO)result[0]).i);
Assert.assertEquals(0, ((TO)result[0]).j);
}
@Test
/**
* dynamically create a class that has a three integer field (set to 1,2,3) and write it, read it back using the TO class that has two
* fields (initialised to 9 and 7)
*/
public void testRemovedField() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ComplexObjectCreator complexObjectCreator = new ComplexObjectCreator("com.betfair.cougar.marshalling.impl.to.TO");
complexObjectCreator.create(bos, new Pair[] {
new Pair<String,String>(Integer.class.getName(),"i"),
new Pair<String,String>(Integer.class.getName(),"j"),
new Pair<String,String>(Integer.class.getName(),"x")
});
ByteArrayClassLoader bcl = new ByteArrayClassLoader(complexObjectCreator.objectType, bos.toByteArray());
Class<?> clazz = Class.forName(complexObjectCreator.objectType, true, bcl);
Object o = clazz.newInstance();
Field f = clazz.getField("i");
f.set(o, 1);
f = clazz.getField("j");
f.set(o, 2);
f = clazz.getField("x");
f.set(o, 3);
Parameter[] params = new Parameter[1];
params[0] = new Parameter("TO", new ParameterType(TO.class,null),false);
bos.reset();
CougarObjectOutput hoo = ioFactory.newCougarObjectOutput(bos, CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED);
cut.writeArgs(params, new Object[] {o}, hoo);
hoo.flush();
params = new Parameter[1];
params[0] = new Parameter("TO", new ParameterType(TO.class,null) ,false);
Object[] result = cut.readArgs(params, ioFactory.newCougarObjectInput(new ByteArrayInputStream(bos.toByteArray()), CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED));
Assert.assertEquals(1, result.length);
Assert.assertEquals(result[0].getClass(), TO.class);
Assert.assertFalse(TO.class.equals(clazz));
Assert.assertEquals(1, ((TO)result[0]).i);
Assert.assertEquals(2, ((TO)result[0]).j);
}
@Test
/**
* dynamically create a class that has an enum with two values (initially set to TWO), write it, and read it back using the EnumTO class that now has three
* enum types (set to THREE)
*/
public void testEnumAdded() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
EnumCreator enumCreator = new EnumCreator("com.betfair.cougar.marshalling.impl.to.EnumType" ,new String[] {"ONE","TWO"});
enumCreator.create(bos);
ByteArrayClassLoader twoEnum = new ByteArrayClassLoader(EnumType.class.getName(), bos.toByteArray());
Class<Enum> twoEnumClass = (Class<Enum>) Class.forName("com.betfair.cougar.marshalling.impl.to.EnumType",true,twoEnum);
Enum TWO = Enum.valueOf(twoEnumClass, "TWO");
bos.reset();
ComplexObjectCreator complexObjectCreator = new ComplexObjectCreator("com.betfair.cougar.marshalling.impl.to.EnumTO");
complexObjectCreator.create(bos, new Pair[]{ new Pair<String,String>("com.betfair.cougar.marshalling.impl.to.EnumType","enumType")});
ByteArrayClassLoader bcl = new ByteArrayClassLoader(twoEnum,"com.betfair.cougar.marshalling.impl.to.EnumTO", bos.toByteArray());
Class<?> enumTOClass = Class.forName("com.betfair.cougar.marshalling.impl.to.EnumTO", true, bcl);
Object enumTO = enumTOClass.newInstance();
Field f = enumTOClass.getField("enumType");
f.set(enumTO, TWO);
Parameter[] params = new Parameter[1];
params[0] = new Parameter("EnumTO", new ParameterType(EnumTO.class,null),false);
bos.reset();
CougarObjectOutput hoo = ioFactory.newCougarObjectOutput(bos, CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED);
cut.writeArgs(params, new Object[] {enumTO}, hoo);
hoo.flush();
Object[] result = cut.readArgs(params, ioFactory.newCougarObjectInput(new ByteArrayInputStream(bos.toByteArray()), CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED));
Assert.assertEquals(1, result.length);
Assert.assertEquals(result[0].getClass(), EnumTO.class);
Assert.assertFalse(EnumTO.class.equals(enumTOClass));
Assert.assertEquals(new EnumTO().enumType, EnumType.THREE);
Assert.assertEquals(EnumType.TWO, ((EnumTO)result[0]).enumType);
}
@Test(expected=TranscriptionException.class)
/**
* dynamically create a class that has an enum with four values (initially set to FOUR), write it, and read it back using the EnumTO class that now has three
* enum types (with FOUR being removed)
*/
public void testEnumRemoved() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
EnumCreator enumCreator = new EnumCreator("com.betfair.cougar.marshalling.impl.to.EnumType" ,new String[] {"ONE","TWO","THREE","FOUR"});
enumCreator.create(bos);
ByteArrayClassLoader enumCL = new ByteArrayClassLoader("com.betfair.cougar.marshalling.impl.to.EnumType",bos.toByteArray());
Class<Enum> enumClass = (Class<Enum>) Class.forName("com.betfair.cougar.marshalling.impl.to.EnumType",true,enumCL);
Enum FOUR = Enum.valueOf(enumClass, "FOUR");
bos.reset();
ComplexObjectCreator complex = new ComplexObjectCreator("com.betfair.cougar.marshalling.impl.to.EnumTO");
complex.create(bos, new Pair[] {new Pair<String,String>("com.betfair.cougar.marshalling.impl.to.EnumType","enumType")});
ByteArrayClassLoader bcl = new ByteArrayClassLoader(enumCL, "com.betfair.cougar.marshalling.impl.to.EnumTO", bos.toByteArray());
Class<?> clazz = Class.forName("com.betfair.cougar.marshalling.impl.to.EnumTO", true, bcl);
Object o = clazz.newInstance();
Field f = clazz.getField("enumType");
f.set(o, FOUR);
Parameter[] params = new Parameter[1];
params[0] = new Parameter("EnumTO", new ParameterType(EnumTO.class,null),false);
bos.reset();
CougarObjectOutput hoo = ioFactory.newCougarObjectOutput(bos, CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED);
cut.writeArgs(params, new Object[] {o}, hoo);
hoo.flush();
Object[] result = cut.readArgs(params, ioFactory.newCougarObjectInput(new ByteArrayInputStream(bos.toByteArray()), CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED));
}
@Test
/**
* A complex type, where the composed type has gone from having one field to having two fields
*/
public void testFieldAdded() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ComplexObjectCreator complexObjectCreator = new ComplexObjectCreator("com.betfair.cougar.marshalling.impl.to.TO");
complexObjectCreator.create(bos, new Pair[] {
new Pair<String,String>(Integer.class.getName(),"i"),
});
//Use the binary class loader to load ComplexTO so that we can wire it up to the ClassLoader that loads the composed (TO) object
ByteArrayClassLoader cl = new ByteArrayClassLoader(complexObjectCreator.objectType,bos.toByteArray());
bos.reset();
InputStream is = ComplexTO.class.getResourceAsStream(ComplexTO.class.getSimpleName()+".class");
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = is.read(buffer)) != -1) {
bos.write(buffer,0,bytesRead);
}
ByteArrayClassLoader complexTOCL = new ByteArrayClassLoader(cl,ComplexTO.class.getName(), bos.toByteArray());
Class<?> clazz = Class.forName(ComplexTO.class.getName(),true,complexTOCL);
Object o = clazz.newInstance();
Field f = clazz.getField("to");
Field toField = f.get(o).getClass().getField("i");
toField.set(f.get(o), 12);
try {
f.getDeclaringClass().getField("j");
Assert.fail();
}
catch (NoSuchFieldException e) {
//just testing that this really is a different class to TO.java
}
Parameter[] params = new Parameter[1];
params[0] = new Parameter("ComplexTO", new ParameterType(clazz,null),false);
bos.reset();
CougarObjectOutput hoo = ioFactory.newCougarObjectOutput(bos, CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED);
cut.writeArgs(params, new Object[]{o}, hoo);
hoo.flush();
params = new Parameter[1];
params[0] = new Parameter("ComplexTO", new ParameterType(ComplexTO.class,null),false);
Object[] result = cut.readArgs(params, ioFactory.newCougarObjectInput(new ByteArrayInputStream(bos.toByteArray()), CougarProtocol.TRANSPORT_PROTOCOL_VERSION_MAX_SUPPORTED));
Assert.assertEquals(12, ((ComplexTO)result[0]).to.i);
Assert.assertEquals(0, ((ComplexTO)result[0]).to.j);
}
}