package org.jboss.cache.marshall;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.factories.ComponentRegistry;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.interceptors.Interceptor;
import org.jboss.cache.interceptors.MarshalledValueInterceptor;
import org.jboss.cache.loader.DummyInMemoryCacheLoader;
import org.jboss.cache.misc.TestingUtil;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeModified;
import org.jboss.cache.notifications.event.NodeModifiedEvent;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.Map;
/**
* Tests implicit marshalled values
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 2.1.0
*/
@Test(groups = "functional")
public class MarshalledValueTest
{
private CacheSPI<Object, Object> cache1, cache2;
private MarshalledValueListenerInterceptor mvli;
@BeforeMethod
public void setUp() throws CloneNotSupportedException
{
cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC, false), false);
if (cache1.getConfiguration().getBuddyReplicationConfig() != null)
cache1.getConfiguration().setBuddyReplicationConfig(null);
cache1.getConfiguration().setUseLazyDeserialization(true);
cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(cache1.getConfiguration().clone(), false);
cache1.start();
cache2.start();
assert TestingUtil.findInterceptor(cache1, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!";
assert TestingUtil.findInterceptor(cache2, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!";
mvli = new MarshalledValueListenerInterceptor();
cache1.addInterceptor(mvli, MarshalledValueInterceptor.class);
}
@AfterMethod
public void tearDown()
{
TestingUtil.killCaches(cache1, cache2);
Pojo.serializationCount = 0;
Pojo.deserializationCount = 0;
}
private void assertOnlyOneRepresentationExists(MarshalledValue mv)
{
assert (mv.instance != null && mv.raw == null) || (mv.instance == null && mv.raw != null) : "Only instance or raw representations should exist in a MarshalledValue; never both";
}
private void assertSerialized(MarshalledValue mv)
{
assert mv.raw != null : "Should be serialized";
}
private void assertDeserialized(MarshalledValue mv)
{
assert mv.instance != null : "Should be deserialized";
}
private void assertSerializationCounts(int serializationCount, int deserializationCount)
{
assert Pojo.serializationCount == serializationCount : "Serialization count: expected " + serializationCount + " but was " + Pojo.serializationCount;
assert Pojo.deserializationCount == deserializationCount : "Deserialization count: expected " + deserializationCount + " but was " + Pojo.deserializationCount;
}
public void testNonSerializable()
{
try
{
cache1.put("/a", "Hello", new Object());
assert false : "Should have failed";
}
catch (CacheException expected)
{
}
assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor";
try
{
cache1.put("/a", new Object(), "Hello");
assert false : "Should have failed";
}
catch (CacheException expected)
{
}
assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor";
}
public void testNodeReleaseObjectValueReferences()
{
Pojo value = new Pojo();
cache1.put("/a", "key", value);
assertSerializationCounts(1, 0);
NodeSPI<Object, Object> node = cache1.getNode("/a");
Object o = node.getDirect("key");
assert o instanceof MarshalledValue;
MarshalledValue mv = (MarshalledValue) o;
assertDeserialized(mv);
assert node.get("key").equals(value);
assertDeserialized(mv);
assertSerializationCounts(1, 0);
node.releaseObjectReferences(false);
assertSerializationCounts(2, 0);
assertOnlyOneRepresentationExists(mv);
assertSerialized(mv);
// now on cache 2
node = cache2.getNode("/a");
o = node.getDirect("key");
assert o instanceof MarshalledValue;
mv = (MarshalledValue) o;
assertSerialized(mv); // this proves that unmarshalling on the recipient cache instance is lazy
assert node.get("key").equals(value);
assertDeserialized(mv);
assertSerializationCounts(2, 1);
node.releaseObjectReferences(false);
assertSerializationCounts(2, 1);
assertOnlyOneRepresentationExists(mv);
assertSerialized(mv);
}
public void testNodeReleaseObjectKeyReferences() throws IOException, ClassNotFoundException
{
Pojo key = new Pojo();
cache1.put("/a", key, "value");
assertSerializationCounts(1, 0);
NodeSPI<Object, Object> node = cache1.getNode("/a");
Object o = node.getKeysDirect().iterator().next();
assert o instanceof MarshalledValue;
MarshalledValue mv = (MarshalledValue) o;
assertDeserialized(mv);
assert node.get(key).equals("value");
assertDeserialized(mv);
assertSerializationCounts(1, 0);
node.releaseObjectReferences(false);
assertSerializationCounts(2, 0);
assertOnlyOneRepresentationExists(mv);
assertSerialized(mv);
// now on cache 2
node = cache2.getNode("/a");
o = node.getKeysDirect().iterator().next();
assert o instanceof MarshalledValue;
mv = (MarshalledValue) o;
assertSerialized(mv);
assert node.get(key).equals("value");
assertSerializationCounts(2, 1);
assertDeserialized(mv);
node.releaseObjectReferences(false);
assertOnlyOneRepresentationExists(mv);
assertSerialized(mv);
assertSerializationCounts(2, 1);
}
public void testEqualsAndHashCode() throws Exception
{
Pojo pojo = new Pojo();
MarshalledValue mv = new MarshalledValue(pojo);
assertDeserialized(mv);
int oldHashCode = mv.hashCode();
mv.serialize();
assertSerialized(mv);
assert oldHashCode == mv.hashCode();
MarshalledValue mv2 = new MarshalledValue(pojo);
assertSerialized(mv);
assertDeserialized(mv2);
assert mv2.hashCode() == oldHashCode;
assert mv.equals(mv2);
}
public void assertUseOfMagicNumbers() throws Exception
{
Pojo pojo = new Pojo();
MarshalledValue mv = new MarshalledValue(pojo);
Configuration c = new Configuration();
ComponentRegistry cr = new ComponentRegistry(c);
Marshaller marshaller = new CacheMarshaller210();
cr.registerComponent(marshaller, Marshaller.class);
// Wire the marshaller
cr.start();
// start the test
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
marshaller.objectToObjectStream(mv, out);
out.close();
bout.close();
// check that the rest just contains a byte stream which a MarshalledValue will be able to deserialize.
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin);
assert in.read() == CacheMarshaller200.MAGICNUMBER_MARSHALLEDVALUE;
MarshalledValue recreated = new MarshalledValue();
recreated.readExternal(in);
// there should be nothing more
assert in.available() == 0;
in.close();
bin.close();
assertSerialized(recreated);
assert recreated.equals(mv);
// since both objects being compared are serialized, the equals() above should just compare byte arrays.
assertSerialized(recreated);
assertOnlyOneRepresentationExists(recreated);
}
public void testCacheLoaders() throws CloneNotSupportedException
{
tearDown();
cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false);
cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false);
CacheLoaderConfig clc = new CacheLoaderConfig();
CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig();
iclc.setClassName(DummyInMemoryCacheLoader.class.getName());
clc.addIndividualCacheLoaderConfig(iclc);
cache1.getConfiguration().setCacheLoaderConfig(clc);
cache2.getConfiguration().setCacheLoaderConfig(clc.clone());
cache1.getConfiguration().setUseLazyDeserialization(true);
cache2.getConfiguration().setUseLazyDeserialization(true);
cache1.start();
cache2.start();
TestingUtil.blockUntilViewsReceived(60000, cache1, cache2);
Pojo pojo = new Pojo();
cache1.put("/a", "key", pojo);
assertSerializationCounts(1, 0);
cache2.get("/a", "key");
assertSerializationCounts(1, 1);
}
public void testCallbackValues()
{
Listener l = new Listener();
cache1.addCacheListener(l);
Pojo pojo = new Pojo();
cache1.put("/a", "key", pojo);
assert l.modData.size() == 1;
assert l.modData.get("key") instanceof Pojo;
assertSerializationCounts(1, 0);
}
public void testRemoteCallbackValues()
{
Listener l = new Listener();
cache2.addCacheListener(l);
Pojo pojo = new Pojo();
cache1.put("/a", "key", pojo);
assert l.modData.size() == 1;
pojo = (Pojo) l.modData.get("key");
assert pojo != null;
assertSerializationCounts(1, 1);
}
@CacheListener
public static class Listener
{
Map modData;
@NodeModified
public void nodeModified(NodeModifiedEvent e)
{
if (!e.isPre()) modData = e.getData();
}
}
class MarshalledValueListenerInterceptor extends Interceptor
{
int invocationCount = 0;
public Object invoke(InvocationContext ctx) throws Throwable
{
if (MethodDeclarations.isPutMethod(ctx.getMethodCall().getMethodId()))
{
invocationCount++;
MethodCall mc = ctx.getMethodCall();
for (Object arg : mc.getArgs())
{
if (arg instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) arg);
}
Object retval = nextInterceptor(ctx);
if (retval instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) retval);
return retval;
}
else return nextInterceptor(ctx);
}
}
public static class Pojo implements Externalizable
{
int i;
boolean b;
static int serializationCount, deserializationCount;
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pojo pojo = (Pojo) o;
if (b != pojo.b) return false;
if (i != pojo.i) return false;
return true;
}
public int hashCode()
{
int result;
result = i;
result = 31 * result + (b ? 1 : 0);
return result;
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(i);
out.writeBoolean(b);
serializationCount++;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
i = in.readInt();
b = in.readBoolean();
deserializationCount++;
}
}
}