package org.infinispan.marshall.core;
import static org.infinispan.test.TestingUtil.createMapEntry;
import static org.infinispan.test.TestingUtil.extractComponent;
import static org.infinispan.test.TestingUtil.extractGlobalMarshaller;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.infinispan.Cache;
import org.infinispan.commons.marshall.NotSerializableException;
import org.infinispan.commons.marshall.StreamingMarshaller;
import org.infinispan.commons.marshall.WrappedByteArray;
import org.infinispan.commons.marshall.WrappedBytes;
import org.infinispan.commons.util.ObjectDuplicator;
import org.infinispan.compat.TypeConverter;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder;
import org.infinispan.test.Exceptions;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* Tests implicit marshalled values
*
* @author Manik Surtani (<a href="mailto:manik AT jboss DOT org">manik AT jboss DOT org</a>)
* @author Mircea.Markus@jboss.com
* @since 4.0
*/
@Test(groups = "functional", testName = "marshall.core.MarshalledValueTest")
public class StoreAsBinaryTest extends MultipleCacheManagersTest {
private static final Log log = LogFactory.getLog(StoreAsBinaryTest.class);
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder replSync = getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, false);
replSync.dataContainer().storeAsBinary().enable();
createClusteredCaches(2, "replSync", replSync);
}
@Override
@AfterClass
protected void destroy() {
super.destroy();
}
@BeforeMethod
public void resetSerializationCounts() {
Pojo.serializationCount = 0;
Pojo.deserializationCount = 0;
}
public void testNonSerializable() {
Cache<Object, Object> cache1 = cache(0, "replSync");
cache(1, "replSync");
Exceptions.expectException(NotSerializableException.class, () -> cache1.put("Hello", new Object()));
Exceptions.expectException(NotSerializableException.class, () -> cache1.put(new Object(), "Hello"));
}
public void testReleaseObjectValueReferences() {
Cache<String, Object> cache1 = cache(0, "replSync");
Cache<String, Object> cache2 = cache(1, "replSync");
assertTrue(cache1.isEmpty());
Pojo value = new Pojo();
cache1.put("key", value);
assertTrue(cache1.containsKey("key"));
DataContainer dc1 = extractComponent(cache1, DataContainer.class);
//noinspection unchecked
TypeConverter<Object, Object, Object, Object> converter = cache1.getAdvancedCache().getComponentRegistry().getComponent(TypeConverter.class);
InternalCacheEntry ice = dc1.get(converter.boxKey("key"));
Object o = ice.getValue();
assertTrue(o instanceof WrappedByteArray);
assertEquals(value, cache1.get("key"));
assertEquals(value, converter.unboxValue(o));
// now on cache 2
DataContainer dc2 = extractComponent(cache2, DataContainer.class);
ice = dc2.get(converter.boxKey("key"));
o = ice.getValue();
assertTrue(o instanceof WrappedByteArray);
assertEquals(value, cache2.get("key"));
assertEquals(value, converter.unboxValue(o));
}
public void testReleaseObjectKeyReferences() {
Cache<Object, String> cache1 = cache(0, "replSync");
Cache<Object, String> cache2 = cache(1, "replSync");
Pojo key = new Pojo();
cache1.put(key, "value");
DataContainer dc1 = extractComponent(cache1, DataContainer.class);
Object o = dc1.keySet().iterator().next();
assertEquals("value", cache1.get(key));
// now on cache 2
DataContainer dc2 = extractComponent(cache2, DataContainer.class);
o = dc2.keySet().iterator().next();
assertEquals("value", cache2.get(key));
}
public void testKeySetValuesEntrySetCollectionReferences() {
Cache<Object, Object> cache1 = cache(0, "replSync");
Cache<Object, Object> cache2 = cache(1, "replSync");
Pojo key1 = new Pojo(1), value1 = new Pojo(11), key2 = new Pojo(2), value2 = new Pojo(22);
String key3 = "3", value3 = "three";
cache1.put(key1, value1);
cache1.put(key2, value2);
cache1.put(key3, value3);
Set<Object> expKeys = new HashSet<>();
expKeys.add(key1);
expKeys.add(key2);
expKeys.add(key3);
Set<Object> expValues = new HashSet<>();
expValues.add(value1);
expValues.add(value2);
expValues.add(value3);
Set<Object> expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
Set<Object> expValueEntries = ObjectDuplicator.duplicateSet(expValues);
Set<Object> keys = cache2.keySet();
for (Object key : keys) assertTrue(expKeys.remove(key));
assertTrue("Did not see keys " + expKeys + " in iterator!", expKeys.isEmpty());
Collection values = cache2.values();
for (Object key : values) assertTrue(expValues.remove(key));
assertTrue("Did not see keys " + expValues + " in iterator!", expValues.isEmpty());
Set<Map.Entry<Object, Object>> entries = cache2.entrySet();
for (Map.Entry entry : entries) {
assertTrue(expKeyEntries.remove(entry.getKey()));
assertTrue(expValueEntries.remove(entry.getValue()));
}
assertTrue("Did not see keys " + expKeyEntries + " in iterator!", expKeyEntries.isEmpty());
assertTrue("Did not see keys " + expValueEntries + " in iterator!", expValueEntries.isEmpty());
}
public void testUnsupportedKeyValueCollectionOperations() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Object> keys = cache(0, "replSync").keySet();
Collection<Object> values = cache(0, "replSync").values();
//noinspection unchecked
Collection<Object>[] collections = new Collection[]{keys, values};
Object newObj = new Object();
List<Object> newObjCol = new ArrayList<>();
newObjCol.add(newObj);
for (Collection<Object> col : collections) {
Exceptions.expectException(UnsupportedOperationException.class, () -> col.add(newObj));
Exceptions.expectException(UnsupportedOperationException.class, () -> col.addAll(newObjCol));
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testAddMethodsForEntryCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
entries.add(createMapEntry("4", "four"));
}
public void testRemoveMethodOfKeyValueEntryCollections() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Object> keys = cache(0, "replSync").keySet();
keys.remove(key1);
assertEquals(2, cache(0, "replSync").size());
Collection<Object> values = cache(0, "replSync").values();
values.remove(value2);
assertEquals(1, cache(0, "replSync").size());
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
entries.remove(TestingUtil.<Object, Object>createMapEntry(key3, value3));
assertEquals(0, cache(0, "replSync").size());
}
public void testClearMethodOfKeyCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Object> keys = cache(0, "replSync").keySet();
keys.clear();
assertEquals(0, cache(0, "replSync").size());
}
public void testClearMethodOfValuesCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Collection<Object> values = cache(0, "replSync").values();
values.clear();
assertEquals(0, cache(0, "replSync").size());
}
public void testClearMethodOfEntryCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
entries.clear();
assertEquals(0, cache(0, "replSync").size());
}
public void testRemoveAllMethodOfKeyCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<String> keyCollection = new ArrayList<>(2);
keyCollection.add(key2);
keyCollection.add(key3);
Collection<Object> keys = cache(0, "replSync").keySet();
keys.removeAll(keyCollection);
assertEquals(1, cache(0, "replSync").size());
}
public void testRemoveAllMethodOfValuesCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<String> valueCollection = new ArrayList<>(2);
valueCollection.add(value1);
valueCollection.add(value2);
Collection<Object> values = cache(0, "replSync").values();
values.removeAll(valueCollection);
assertEquals(1, cache(0, "replSync").size());
}
public void testRemoveAllMethodOfEntryCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<Map.Entry> entryCollection = new ArrayList<>(2);
entryCollection.add(createMapEntry(key1, value1));
entryCollection.add(createMapEntry(key3, value3));
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
entries.removeAll(entryCollection);
assertEquals(1, cache(0, "replSync").size());
}
public void testRetainAllMethodOfKeyCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<String> keyCollection = new ArrayList<>(2);
keyCollection.add(key2);
keyCollection.add(key3);
keyCollection.add("6");
Collection<Object> keys = cache(0, "replSync").keySet();
keys.retainAll(keyCollection);
assertEquals(2, cache(0, "replSync").size());
}
public void testRetainAllMethodOfValuesCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<String> valueCollection = new ArrayList<>(2);
valueCollection.add(value1);
valueCollection.add(value2);
valueCollection.add("5");
Collection<Object> values = cache(0, "replSync").values();
values.retainAll(valueCollection);
assertEquals(2, cache(0, "replSync").size());
}
public void testRetainAllMethodOfEntryCollection() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
List<Map.Entry> entryCollection = new ArrayList<>(3);
entryCollection.add(createMapEntry(key1, value1));
entryCollection.add(createMapEntry(key3, value3));
entryCollection.add(createMapEntry("4", "5"));
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
entries.retainAll(entryCollection);
assertEquals(2, cache(0, "replSync").size());
}
public void testEntrySetValueFromEntryCollections() {
final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
String newString = "new-value";
for (Map.Entry<Object, Object> entry : entries) {
entry.setValue(newString);
}
assertEquals(3, cache(0, "replSync").size());
assertEquals(newString, cache(0, "replSync").get(key1));
assertEquals(newString, cache(0, "replSync").get(key2));
assertEquals(newString, cache(0, "replSync").get(key3));
}
public void testKeyValueEntryCollections() {
String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
Map<String, String> m = new HashMap<>();
m.put(key1, value1);
m.put(key2, value2);
m.put(key3, value3);
cache(0, "replSync").putAll(m);
assert 3 == cache(0, "replSync").size() && 3 == cache(0, "replSync").keySet().size() && 3 == cache(0, "replSync").values().size() && 3 == cache(0, "replSync").entrySet().size();
Set<Object> expKeys = new HashSet<>();
expKeys.add(key1);
expKeys.add(key2);
expKeys.add(key3);
Set<Object> expValues = new HashSet<>();
expValues.add(value1);
expValues.add(value2);
expValues.add(value3);
Set expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
Set expValueEntries = ObjectDuplicator.duplicateSet(expValues);
Set<Object> keys = cache(0, "replSync").keySet();
for (Object key : keys) {
assert expKeys.remove(key);
}
assert expKeys.isEmpty() : "Did not see keys " + expKeys + " in iterator!";
Collection<Object> values = cache(0, "replSync").values();
for (Object value : values) {
assert expValues.remove(value);
}
assert expValues.isEmpty() : "Did not see keys " + expValues + " in iterator!";
Set<Map.Entry<Object, Object>> entries = cache(0, "replSync").entrySet();
for (Map.Entry entry : entries) {
assert expKeyEntries.remove(entry.getKey());
assert expValueEntries.remove(entry.getValue());
}
assert expKeyEntries.isEmpty() : "Did not see keys " + expKeyEntries + " in iterator!";
assert expValueEntries.isEmpty() : "Did not see keys " + expValueEntries + " in iterator!";
}
public void testEqualsAndHashCode() throws Exception {
StreamingMarshaller marshaller = extractGlobalMarshaller(cache(0).getCacheManager());
Pojo pojo = new Pojo();
WrappedBytes wb = new WrappedByteArray(marshaller.objectToByteBuffer(pojo));
WrappedBytes wb2 = new WrappedByteArray(marshaller.objectToByteBuffer(pojo));
assertTrue(wb2.hashCode() == wb.hashCode());
assertEquals(wb, wb2);
}
public void testMarshallValueWithCustomReadObjectMethod() {
Cache<Object, Object> cache1 = cache(0, "replSync");
Cache<Object, Object> cache2 = cache(1, "replSync");
CustomReadObjectMethod obj = new CustomReadObjectMethod();
cache1.put("ab-key", obj);
assertEquals(obj, cache2.get("ab-key"));
ObjectThatContainsACustomReadObjectMethod anotherObj = new ObjectThatContainsACustomReadObjectMethod();
anotherObj.anObjectWithCustomReadObjectMethod = obj;
cache1.put("cd-key", anotherObj);
assertEquals(anotherObj, cache2.get("cd-key"));
}
/**
* Run this on a separate cache as it creates and stops stores, which might affect other tests.
*/
public void testStores() {
ConfigurationBuilder cacheCofig = getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, false);
cacheCofig.dataContainer().storeAsBinary().enable();
DummyInMemoryStoreConfigurationBuilder dimcs = new DummyInMemoryStoreConfigurationBuilder(cacheCofig.persistence());
dimcs.storeName(getClass().getSimpleName());
cacheCofig.persistence().addStore(dimcs);
defineConfigurationOnAllManagers("replSync2", cacheCofig);
waitForClusterToForm("replSync2");
Cache<Object, Object> cache1 = cache(0, "replSync2");
Cache<Object, Object> cache2 = cache(1, "replSync2");
Pojo pojo = new Pojo();
cache1.put("key", pojo);
assertEquals(pojo, cache2.get("key"));
}
public void testCallbackValues() throws Exception {
Cache<Object, Object> cache1 = cache(0, "replSync");
cache(1, "replSync");
MockListener l = new MockListener();
cache1.addListener(l);
try {
Pojo pojo = new Pojo();
cache1.put("key", pojo);
assertTrue("recieved " + l.newValue.getClass().getName(), l.newValue instanceof Pojo);
} finally {
cache1.removeListener(l);
}
}
public void testRemoteCallbackValues() throws Exception {
Cache<Object, Object> cache1 = cache(0, "replSync");
Cache<Object, Object> cache2 = cache(1, "replSync");
MockListener l = new MockListener();
cache2.addListener(l);
try {
Pojo pojo = new Pojo();
cache1.put("key", pojo);
assertTrue(l.newValue instanceof Pojo);
} finally {
cache2.removeListener(l);
}
}
public void testEvictWithMarshalledValueKey() {
Cache<Object, Object> cache1 = cache(0, "replSync");
cache(1, "replSync");
Pojo pojo = new Pojo();
cache1.put(pojo, pojo);
cache1.evict(pojo);
assertTrue(!cache1.containsKey(pojo));
}
public void testModificationsOnSameCustomKey() {
Cache<Object, Object> cache1 = cache(0, "replSync");
Cache<Object, Object> cache2 = cache(1, "replSync");
Pojo key1 = new Pojo();
log.trace("First put");
cache1.put(key1, "1");
log.trace("Second put");
Pojo key2 = new Pojo();
assertEquals("1", cache2.put(key2, "2"));
}
public void testReturnValueDeserialization() {
Cache<Object, Object> cache1 = cache(0, "replSync");
cache(1, "replSync");
Pojo v1 = new Pojo(1);
cache1.put("1", v1);
Pojo previous = (Pojo) cache1.put("1", new Pojo(2));
assertEquals(v1, previous);
}
@Listener
public static class MockListener {
Object newValue;
@CacheEntryModified
public void modified(CacheEntryModifiedEvent e) {
if (!e.isPre()) newValue = e.getValue();
}
@CacheEntryCreated
public void created(CacheEntryCreatedEvent e) {
if (!e.isPre()) newValue = e.getValue();
}
}
public static class Pojo implements Externalizable, ExternalPojo {
public int i;
static int serializationCount, deserializationCount;
final Log log = LogFactory.getLog(Pojo.class);
private static final long serialVersionUID = -2888014339659501395L;
Pojo(int i) {
this.i = i;
}
public Pojo() {}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pojo pojo = (Pojo) o;
return i == pojo.i;
}
@Override
public int hashCode() {
return i;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(i);
int serCount = updateSerializationCount();
log.trace("serializationCount=" + serCount);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
i = in.readInt();
int deserCount = updateDeserializationCount();
log.trace("deserializationCount=" + deserCount);
}
int updateSerializationCount() {
return ++serializationCount;
}
int updateDeserializationCount() {
return ++deserializationCount;
}
@Override
public String toString() {
return "Pojo{" +
"i=" + i +
'}';
}
}
public static class ObjectThatContainsACustomReadObjectMethod implements Serializable, ExternalPojo {
private static final long serialVersionUID = 1L;
// Integer id;
CustomReadObjectMethod anObjectWithCustomReadObjectMethod;
Integer balance;
// String branch;
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof ObjectThatContainsACustomReadObjectMethod))
return false;
ObjectThatContainsACustomReadObjectMethod acct = (ObjectThatContainsACustomReadObjectMethod) obj;
// if (!safeEquals(id, acct.id))
// return false;
// if (!safeEquals(branch, acct.branch))
// return false;
return safeEquals(balance, acct.balance) && safeEquals(anObjectWithCustomReadObjectMethod, acct.anObjectWithCustomReadObjectMethod);
}
@Override
public int hashCode() {
int result = 17;
// result = result * 31 + safeHashCode(id);
// result = result * 31 + safeHashCode(branch);
result = result * 31 + safeHashCode(balance);
result = result * 31 + safeHashCode(anObjectWithCustomReadObjectMethod);
return result;
}
private static int safeHashCode(Object obj) {
return obj == null ? 0 : obj.hashCode();
}
private static boolean safeEquals(Object a, Object b) {
return (a == b || (a != null && a.equals(b)));
}
}
public static class CustomReadObjectMethod implements Serializable, ExternalPojo {
private static final long serialVersionUID = 1L;
String lastName;
String ssn;
transient boolean deserialized;
CustomReadObjectMethod() {
this("Zamarreno", "234-567-8901");
}
CustomReadObjectMethod(String lastName, String ssn) {
this.lastName = lastName;
this.ssn = ssn;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof CustomReadObjectMethod)) return false;
CustomReadObjectMethod pk = (CustomReadObjectMethod) obj;
return lastName.equals(pk.lastName) && ssn.equals(pk.ssn);
}
@Override
public int hashCode( ) {
int result = 17;
result = result * 31 + lastName.hashCode();
result = result * 31 + ssn.hashCode();
return result;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
deserialized = true;
}
}
}