/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.cache;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.sf.ehcache.CacheManager;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.cache.DefaultViewComputationCacheSource.MissingValueLoader;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.id.UniqueId;
import com.opengamma.transport.DirectFudgeConnection;
import com.opengamma.util.ehcache.EHCacheUtils;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;
import com.opengamma.util.money.Currency;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.test.Timeout;
import com.opengamma.util.tuple.Pair;
@Test(groups = {TestGroup.INTEGRATION, "ehcache"})
public class PrivateToSharedTransferTest {
private CacheManager _cacheManager;
@BeforeClass
public void setUpClass() {
_cacheManager = EHCacheUtils.createTestCacheManager(getClass());
}
@AfterClass
public void tearDownClass() {
EHCacheUtils.shutdownQuiet(_cacheManager);
}
@BeforeMethod
public void setUp() {
EHCacheUtils.clear(_cacheManager);
}
//-------------------------------------------------------------------------
private static ValueSpecification[] createValueSpecifications(final int count) {
final ValueSpecification[] specs = new ValueSpecification[count];
final ComputationTargetSpecification target = ComputationTargetSpecification.of(Currency.USD);
for (int i = 0; i < specs.length; i++) {
specs[i] = new ValueSpecification(Integer.toString(i), target, ValueProperties.with(ValuePropertyNames.FUNCTION, "Bar").get());
}
return specs;
}
private static FudgeMessageStoreFactory createInMemoryFudgeMessageStoreFactory(final FudgeContext fudgeContext) {
return new DefaultFudgeMessageStoreFactory(new InMemoryBinaryDataStoreFactory(), fudgeContext);
}
@Test
public void testMissingValueLoader_noCallback() {
final IdentifierMap identifiers = new InMemoryIdentifierMap();
final DefaultViewComputationCacheSource source = new DefaultViewComputationCacheSource(identifiers,
FudgeContext.GLOBAL_DEFAULT, createInMemoryFudgeMessageStoreFactory(FudgeContext.GLOBAL_DEFAULT));
final ViewComputationCache cache = source.getCache(UniqueId.of("Test", "ViewCycle"), "Default");
final ValueSpecification[] specs = createValueSpecifications(4);
cache.putPrivateValue(new ComputedValue(specs[0], "Zero"));
cache.putSharedValue(new ComputedValue(specs[1], "One"));
Object value = cache.getValue(specs[0]);
assertEquals("Zero", value);
value = cache.getValue(specs[1]);
assertEquals("One", value);
value = cache.getValue(specs[2]);
assertNull(value);
Collection<Pair<ValueSpecification, Object>> values = cache.getValues(Arrays.asList(specs[2], specs[3]));
assertNotNull(values);
assertTrue(values.isEmpty());
}
@Test
public void testMissingValueLoader_withCallback() {
final IdentifierMap identifiers = new InMemoryIdentifierMap();
final Set<Integer> missing = new HashSet<Integer>();
final DefaultViewComputationCacheSource source = new DefaultViewComputationCacheSource(identifiers,
FudgeContext.GLOBAL_DEFAULT, createInMemoryFudgeMessageStoreFactory(FudgeContext.GLOBAL_DEFAULT));
final UniqueId viewCycleId = UniqueId.of("Test", "ViewCycle");
source.setMissingValueLoader(new MissingValueLoader() {
@Override
public FudgeMsg findMissingValue(final ViewComputationCacheKey cache, final long identifier) {
assertEquals(viewCycleId, cache.getViewCycleId());
assertEquals("Default", cache.getCalculationConfigurationName());
final ValueSpecification spec = identifiers.getValueSpecification(identifier);
int i = Integer.parseInt(spec.getValueName());
missing.add(i);
switch (i) {
case 0:
return FudgeContext.GLOBAL_DEFAULT.toFudgeMsg("Zero").getMessage();
case 1:
return FudgeContext.GLOBAL_DEFAULT.toFudgeMsg("One").getMessage();
case 2:
return FudgeContext.GLOBAL_DEFAULT.toFudgeMsg("Two").getMessage();
case 3:
return FudgeContext.GLOBAL_DEFAULT.toFudgeMsg("Three").getMessage();
}
return null;
}
@Override
public Map<Long, FudgeMsg> findMissingValues(final ViewComputationCacheKey cache,
final Collection<Long> identifiers) {
final Map<Long, FudgeMsg> map = new HashMap<Long, FudgeMsg>();
for (Long identifier : identifiers) {
map.put(identifier, findMissingValue(cache, identifier));
}
return map;
}
});
final ViewComputationCache cache = source.getCache(viewCycleId, "Default");
final ValueSpecification[] specs = createValueSpecifications(4);
cache.putPrivateValue(new ComputedValue(specs[0], "Zero"));
cache.putSharedValue(new ComputedValue(specs[1], "One"));
Object value = cache.getValue(specs[0]);
assertEquals("Zero", value);
value = cache.getValue(specs[1]);
assertEquals("One", value);
value = cache.getValue(specs[2]);
assertEquals("Two", value);
assertEquals(1, missing.size());
assertTrue(missing.contains(2));
missing.clear();
Collection<Pair<ValueSpecification, Object>> values = cache.getValues(Arrays.asList(specs[2], specs[3]));
assertEquals(2, values.size());
assertTrue(values.contains(Pair.of(specs[2], "Two")));
assertTrue(values.contains(Pair.of(specs[3], "Three")));
assertEquals(2, missing.size());
assertTrue(missing.contains(2));
assertTrue(missing.contains(3));
missing.clear();
values = cache.getValues(Arrays.asList(specs[0], specs[1], specs[2]));
assertEquals(3, values.size());
assertTrue(values.contains(Pair.of(specs[0], "Zero")));
assertTrue(values.contains(Pair.of(specs[1], "One")));
assertTrue(values.contains(Pair.of(specs[2], "Two")));
assertEquals(1, missing.size());
assertTrue(missing.contains(2));
}
@Test
public void testFindMessage() throws InterruptedException {
// Create the test infrastructure
final FudgeContext fudgeContext = OpenGammaFudgeContext.getInstance();
final DefaultViewComputationCacheSource serverCacheSource = new DefaultViewComputationCacheSource(
new InMemoryIdentifierMap(), fudgeContext, createInMemoryFudgeMessageStoreFactory(fudgeContext));
final ViewComputationCacheServer server = new ViewComputationCacheServer(serverCacheSource);
server.getBinaryDataStore().setFindValueTimeout(Timeout.standardTimeoutMillis() * 5);
final DirectFudgeConnection conduit1 = new DirectFudgeConnection(fudgeContext);
conduit1.connectEnd1(server);
final DirectFudgeConnection conduit2 = new DirectFudgeConnection(fudgeContext);
conduit2.connectEnd1(server);
final DirectFudgeConnection conduit3 = new DirectFudgeConnection(fudgeContext);
conduit3.connectEnd1(server);
final RemoteViewComputationCacheSource remoteCacheSource1 = new RemoteViewComputationCacheSource(
new RemoteCacheClient(conduit1.getEnd2()), createInMemoryFudgeMessageStoreFactory(fudgeContext), _cacheManager);
final RemoteViewComputationCacheSource remoteCacheSource2 = new RemoteViewComputationCacheSource(
new RemoteCacheClient(conduit2.getEnd2()), createInMemoryFudgeMessageStoreFactory(fudgeContext), _cacheManager);
final RemoteViewComputationCacheSource remoteCacheSource3 = new RemoteViewComputationCacheSource(
new RemoteCacheClient(conduit3.getEnd2()), createInMemoryFudgeMessageStoreFactory(fudgeContext), _cacheManager);
// Populate the test caches
final ValueSpecification[] specs = createValueSpecifications(10);
UniqueId viewCycleId = UniqueId.of("Test", "ViewCycle");
final ViewComputationCache serverCache = serverCacheSource.getCache(viewCycleId, "Default");
final ViewComputationCache remoteCache1 = remoteCacheSource1.getCache(viewCycleId, "Default");
final ViewComputationCache remoteCache2 = remoteCacheSource2.getCache(viewCycleId, "Default");
final ViewComputationCache remoteCache3 = remoteCacheSource3.getCache(viewCycleId, "Default");
serverCache.putSharedValue(new ComputedValue(specs[0], "Zero"));
serverCache.putPrivateValue(new ComputedValue(specs[1], "One"));
remoteCache1.putPrivateValue(new ComputedValue(specs[2], "Two"));
remoteCache1.putPrivateValue(new ComputedValue(specs[3], "Three"));
remoteCache1.putPrivateValue(new ComputedValue(specs[4], "Four"));
remoteCache2.putPrivateValue(new ComputedValue(specs[3], "Three"));
remoteCache2.putPrivateValue(new ComputedValue(specs[5], "Five"));
remoteCache2.putPrivateValue(new ComputedValue(specs[6], "Six"));
remoteCache3.putPrivateValue(new ComputedValue(specs[7], "Seven"));
// Direct lookup on server
Object value = serverCache.getValue(specs[0]);
assertEquals("Zero", value);
value = serverCache.getValue(specs[1]);
assertEquals("One", value);
// Only identifier lookup messages expected
Thread.sleep(Timeout.standardTimeoutMillis());
assertEquals(3, conduit1.getAndResetMessages1To2());
assertEquals(3, conduit1.getAndResetMessages2To1());
assertEquals(3, conduit2.getAndResetMessages1To2());
assertEquals(3, conduit2.getAndResetMessages2To1());
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(1, conduit3.getAndResetMessages2To1());
// Query for a value elsewhere
value = serverCache.getValue(specs[2]);
assertEquals("Two", value);
// One message sent to each, 1 response (and ack) from client1
Thread.sleep(Timeout.standardTimeoutMillis());
assertEquals(2, conduit1.getAndResetMessages1To2());
assertEquals(1, conduit1.getAndResetMessages2To1());
assertEquals(1, conduit2.getAndResetMessages1To2());
assertEquals(0, conduit2.getAndResetMessages2To1());
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(0, conduit3.getAndResetMessages2To1());
// Query for a value duplicated on multiple nodes
value = serverCache.getValue(specs[3]);
assertEquals("Three", value);
// One message sent to each, 1 response (and ack) from client1 and client2
Thread.sleep(Timeout.standardTimeoutMillis());
assertRange(1, 2, conduit1.getAndResetMessages1To2());
assertRange(0, 1, conduit1.getAndResetMessages2To1());
assertRange(1, 2, conduit2.getAndResetMessages1To2());
assertRange(0, 1, conduit2.getAndResetMessages2To1());
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(0, conduit3.getAndResetMessages2To1());
// Query for a set of values on two different nodes
Collection<Pair<ValueSpecification, Object>> values = serverCache.getValues(Arrays.asList(specs[4], specs[5],
specs[6]));
assertEquals(3, values.size());
assertTrue(values.contains(Pair.of(specs[4], "Four")));
assertTrue(values.contains(Pair.of(specs[5], "Five")));
assertTrue(values.contains(Pair.of(specs[6], "Six")));
// One message sent to each, 1 response (and ack) from client1 and 2
Thread.sleep(Timeout.standardTimeoutMillis());
assertEquals(2, conduit1.getAndResetMessages1To2());
assertEquals(1, conduit1.getAndResetMessages2To1());
assertEquals(2, conduit2.getAndResetMessages1To2());
assertEquals(1, conduit2.getAndResetMessages2To1());
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(0, conduit3.getAndResetMessages2To1());
// Query for a non-existent single value
value = serverCache.getValue(specs[8]);
assertNull(value);
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(0, conduit3.getAndResetMessages2To1());
// Query for one existing and one non-existent value
values = serverCache.getValues(Arrays.asList(specs[7], specs[8]));
assertEquals(1, values.size());
assertTrue(values.contains(Pair.of(specs[7], "Seven")));
assertEquals(2, conduit3.getAndResetMessages1To2());
assertEquals(1, conduit3.getAndResetMessages2To1());
// Query for two non-existing values
values = serverCache.getValues(Arrays.asList(specs[8], specs[9]));
assertNotNull(values);
assertTrue(values.isEmpty());
assertEquals(1, conduit3.getAndResetMessages1To2());
assertEquals(0, conduit3.getAndResetMessages2To1());
}
private void assertRange(final int loInclusive, final int hiInclusive, final int value) {
assertTrue("value " + value + " < " + loInclusive, loInclusive <= value);
assertTrue("value " + value + " > " + hiInclusive, value <= hiInclusive);
}
}