/*
* Copyright Terracotta, Inc.
*
* 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 org.ehcache.impl.internal.store.disk;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.EvictionAdvisor;
import org.ehcache.config.ResourcePools;
import org.ehcache.core.internal.store.StoreConfigurationImpl;
import org.ehcache.config.SizedResourcePool;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.CachePersistenceException;
import org.ehcache.expiry.Expirations;
import org.ehcache.expiry.Expiry;
import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.ehcache.impl.internal.events.TestStoreEventDispatcher;
import org.ehcache.impl.internal.executor.OnDemandExecutionService;
import org.ehcache.impl.internal.persistence.TestDiskResourceService;
import org.ehcache.impl.internal.store.offheap.BasicOffHeapValueHolder;
import org.ehcache.impl.internal.store.offheap.OffHeapValueHolder;
import org.ehcache.core.spi.time.SystemTimeSource;
import org.ehcache.core.spi.time.TimeSource;
import org.ehcache.impl.serialization.JavaSerializer;
import org.ehcache.internal.store.StoreFactory;
import org.ehcache.internal.tier.AuthoritativeTierFactory;
import org.ehcache.internal.tier.AuthoritativeTierSPITest;
import org.ehcache.core.internal.service.ServiceLocator;
import org.ehcache.core.spi.store.Store;
import org.ehcache.core.spi.store.tiering.AuthoritativeTier;
import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.persistence.PersistableResourceService.PersistenceSpaceIdentifier;
import org.ehcache.spi.service.ServiceConfiguration;
import org.ehcache.spi.test.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static org.ehcache.config.ResourceType.Core.DISK;
import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder;
import static org.ehcache.core.internal.service.ServiceLocator.dependencySet;
import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_DISK_SEGMENTS;
import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_WRITER_CONCURRENCY;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* OffHeapStoreSPITest
*/
public class OffHeapDiskStoreSPITest extends AuthoritativeTierSPITest<String, String> {
private AuthoritativeTierFactory<String, String> authoritativeTierFactory;
private final Map<Store<String, String>, String> createdStores = new ConcurrentHashMap<Store<String, String>, String>();
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Rule
public final TestDiskResourceService diskResourceService = new TestDiskResourceService();
@Before
public void setUp() throws Exception {
authoritativeTierFactory = new AuthoritativeTierFactory<String, String>() {
final AtomicInteger index = new AtomicInteger();
@Override
public AuthoritativeTier<String, String> newStore() {
return newStore(null, null, Expirations.noExpiration(), SystemTimeSource.INSTANCE);
}
@Override
public AuthoritativeTier<String, String> newStoreWithCapacity(long capacity) {
return newStore(capacity, null, Expirations.noExpiration(), SystemTimeSource.INSTANCE);
}
@Override
public AuthoritativeTier<String, String> newStoreWithExpiry(Expiry<? super String, ? super String> expiry, TimeSource timeSource) {
return newStore(null, null, expiry, timeSource);
}
@Override
public AuthoritativeTier<String, String> newStoreWithEvictionAdvisor(EvictionAdvisor<String, String> evictionAdvisor) {
return newStore(null, evictionAdvisor, Expirations.noExpiration(), SystemTimeSource.INSTANCE);
}
private AuthoritativeTier<String, String> newStore(Long capacity, EvictionAdvisor<String, String> evictionAdvisor, Expiry<? super String, ? super String> expiry, TimeSource timeSource) {
Serializer<String> keySerializer = new JavaSerializer<String>(getClass().getClassLoader());
Serializer<String> valueSerializer = new JavaSerializer<String>(getClass().getClassLoader());
try {
CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class);
when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build());
String spaceName = "OffheapDiskStore-" + index.getAndIncrement();
PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier(spaceName, cacheConfiguration);
ResourcePools resourcePools = getDiskResourcePool(capacity);
SizedResourcePool diskPool = resourcePools.getPoolForResource(DISK);
MemoryUnit unit = (MemoryUnit)diskPool.getUnit();
Store.Configuration<String, String> config = new StoreConfigurationImpl<String, String>(getKeyType(), getValueType(),
evictionAdvisor, getClass().getClassLoader(), expiry, resourcePools, 0, keySerializer, valueSerializer);
OffHeapDiskStore<String, String> store = new OffHeapDiskStore<String, String>(
diskResourceService.createPersistenceContextWithin(space, "store"),
new OnDemandExecutionService(), null, DEFAULT_WRITER_CONCURRENCY, DEFAULT_DISK_SEGMENTS,
config, timeSource,
new TestStoreEventDispatcher<String, String>(),
unit.toBytes(diskPool.getSize()));
OffHeapDiskStore.Provider.init(store);
createdStores.put(store, spaceName);
return store;
} catch (CachePersistenceException cpex) {
throw new RuntimeException("Error creating persistence context", cpex);
}
}
private ResourcePools getDiskResourcePool(Comparable<Long> capacityConstraint) {
if (capacityConstraint == null) {
capacityConstraint = 10L;
}
return newResourcePoolsBuilder().disk((Long) capacityConstraint, MemoryUnit.MB).build();
}
@Override
public Store.ValueHolder<String> newValueHolder(String value) {
return new BasicOffHeapValueHolder<String>(-1, value, SystemTimeSource.INSTANCE.getTimeMillis(), OffHeapValueHolder.NO_EXPIRE);
}
@Override
public Class<String> getKeyType() {
return String.class;
}
@Override
public Class<String> getValueType() {
return String.class;
}
@Override
public ServiceConfiguration<?>[] getServiceConfigurations() {
try {
CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class);
when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build());
String spaceName = "OffheapDiskStore-" + index.getAndIncrement();
PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier(spaceName, cacheConfiguration);
return new ServiceConfiguration[] {space};
} catch (CachePersistenceException e) {
throw new RuntimeException(e);
}
}
@Override
public ServiceLocator getServiceProvider() {
ServiceLocator serviceLocator = dependencySet().build();
try {
serviceLocator.startAllServices();
} catch (Exception e) {
throw new RuntimeException(e);
}
return serviceLocator;
}
@Override
public String createKey(long seed) {
return Long.toString(seed);
}
@Override
public String createValue(long seed) {
char[] chars = new char[400 * 1024];
Arrays.fill(chars, (char) (0x1 + (seed & 0x7e)));
return new String(chars);
}
@Override
public void close(final Store<String, String> store) {
String spaceName = createdStores.get(store);
try {
OffHeapDiskStore.Provider.close((OffHeapDiskStore<String, String>)store);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
try {
diskResourceService.destroy(spaceName);
} catch (CachePersistenceException ex) {
throw new AssertionError(ex);
} finally {
createdStores.remove(store);
}
}
};
}
@After
public void tearDown() throws CachePersistenceException, IOException {
try {
for (Map.Entry<Store<String, String>, String> entry : createdStores.entrySet()) {
OffHeapDiskStore.Provider.close((OffHeapDiskStore<String, String>) entry.getKey());
diskResourceService.destroy(entry.getValue());
}
} finally {
diskResourceService.stop();
}
}
public static void initStore(final OffHeapDiskStore<?, ?> diskStore) {
OffHeapDiskStore.Provider.init(diskStore);
}
public static void closeStore(final OffHeapDiskStore<?, ?> diskStore) {
try {
OffHeapDiskStore.Provider.close(diskStore);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
protected AuthoritativeTierFactory<String, String> getAuthoritativeTierFactory() {
return authoritativeTierFactory;
}
@Override
protected StoreFactory<String, String> getStoreFactory() {
return getAuthoritativeTierFactory();
}
}