/*
* Created on May 3, 2005
*
* 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.
*
* Copyright @2005 the original author or authors.
*/
package org.springmodules.cache.provider.jcs;
import java.beans.PropertyEditor;
import java.io.Serializable;
import java.lang.reflect.Method;
import junit.framework.TestCase;
import org.apache.jcs.engine.CacheElement;
import org.apache.jcs.engine.CompositeCacheAttributes;
import org.apache.jcs.engine.ElementAttributes;
import org.apache.jcs.engine.behavior.ICacheElement;
import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
import org.apache.jcs.engine.behavior.IElementAttributes;
import org.apache.jcs.engine.control.CompositeCache;
import org.apache.jcs.engine.control.CompositeCacheManager;
import org.apache.jcs.engine.control.group.GroupAttrName;
import org.apache.jcs.engine.control.group.GroupId;
import org.easymock.AbstractMatcher;
import org.easymock.MockControl;
import org.easymock.classextension.MockClassControl;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springmodules.cache.FatalCacheException;
import org.springmodules.cache.provider.CacheAccessException;
import org.springmodules.cache.provider.CacheModelValidator;
import org.springmodules.cache.provider.CacheNotFoundException;
import org.springmodules.cache.provider.ReflectionCacheModelEditor;
import org.springmodules.cache.provider.jcs.JcsFlushingModel.CacheStruct;
/**
* <p>
* Unit Tests for <code>{@link JcsFacade}</code>.
* </p>
*
* @author Alex Ruiz
*/
public final class JcsFacadeTests extends TestCase {
protected class CacheElementMatcher extends AbstractMatcher {
/**
* @see AbstractMatcher#argumentMatches(Object, Object)
*/
protected boolean argumentMatches(Object expected, Object actual) {
if (!(expected instanceof CacheElement)) {
throw new IllegalArgumentException(
"Element matcher only evaluates instances of <"
+ CacheElement.class.getName() + ">");
}
if (!(actual instanceof CacheElement)) {
return false;
}
CacheElement expectedElement = (CacheElement) expected;
CacheElement actualElement = (CacheElement) actual;
return equals(expectedElement, actualElement);
}
private boolean equals(CacheElement expected, CacheElement actual) {
if (expected == actual) {
return true;
}
if (!ObjectUtils.nullSafeEquals(expected.getKey(), actual.getKey())) {
return false;
}
if (!ObjectUtils.nullSafeEquals(expected.getVal(), actual.getVal())) {
return false;
}
return true;
}
}
private class CacheEntry {
String group;
Serializable key;
Object value;
public CacheEntry(Serializable newKey, String newGroup, Object newValue) {
super();
key = newKey;
group = newGroup;
value = newValue;
}
}
private static final String CACHE_NAME = "testCache";
private CompositeCache cache;
private MockClassControl cacheControl;
private CacheEntry[] cacheEntries;
private CompositeCacheManager cacheManager;
private MockClassControl cacheManagerControl;
private JcsCachingModel cachingModel;
private JcsFlushingModel flushingModel;
private JcsFacade jcsFacade;
public JcsFacadeTests(String name) {
super(name);
}
/**
* Verifies that the method
* <code>{@link JcsFacade#modelValidator()}</code> returns an an
* instance of <code>{@link JcsModelValidator}</code> not equal to
* <code>null</code>.
*/
public void testGetCacheModelValidator() {
CacheModelValidator validator = jcsFacade.modelValidator();
assertNotNull(validator);
assertEquals(JcsModelValidator.class, validator.getClass());
}
public void testGetCacheWithExistingCache() {
setUpCacheAdministratorAndCache();
assertSame(cacheManager.getCache(CACHE_NAME), jcsFacade
.getCache(CACHE_NAME));
}
public void testGetCacheWithNotExistingCache() throws Exception {
Method getCacheMethod = this
.getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCacheMethod);
expectCacheIsNotFound();
replay();
try {
jcsFacade.getCache(CACHE_NAME);
fail();
} catch (CacheNotFoundException exception) {
// expecting this exception
}
verify();
}
public void testGetCachingModelEditor() {
PropertyEditor editor = jcsFacade.getCachingModelEditor();
assertNotNull(editor);
assertEquals(ReflectionCacheModelEditor.class, editor.getClass());
ReflectionCacheModelEditor modelEditor = (ReflectionCacheModelEditor) editor;
assertEquals(JcsCachingModel.class, modelEditor.getCacheModelClass());
assertNull(modelEditor.getCacheModelPropertyEditors());
}
public void testGetFlushingModelEditor() {
PropertyEditor editor = jcsFacade.getFlushingModelEditor();
assertNotNull(editor);
assertEquals(JcsFlushingModelEditor.class, editor.getClass());
}
public void testGetKeyWithGroupName() {
cachingModel.setGroup("empire");
Serializable key = "Vader";
GroupId groupId = new GroupId(CACHE_NAME, cachingModel.getGroup());
GroupAttrName expected = new GroupAttrName(groupId, key);
Serializable actual = jcsFacade.getKey(key, cachingModel);
assertEquals(expected, actual);
}
/**
* Verifies that the method
* <code>{@link JcsFacade#getKey(Serializable, JcsCachingModel)}</code>
* creates a key that does not contain the group if the given cache model does
* not specify any group.
*/
public void testGetKeyWithoutGroupName() {
Serializable key = "FalconMillenium";
assertEquals(key, jcsFacade.getKey(key, cachingModel));
}
public void testIsSerializableCacheElementRequired() {
assertTrue(jcsFacade.isSerializableCacheElementRequired());
}
public void testOnFlushCacheCacheStructArrayEqualToNull() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
updateCache();
int expected = cache.getSize();
flushingModel.setCacheStructs(null);
jcsFacade.onFlushCache(flushingModel);
// the cache should not be flushed.
assertEquals(expected, cache.getSize());
}
public void testOnFlushCacheWhenCacheAccessThrowsException() throws Exception {
Method getCache = getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCache);
Method removeAll = CompositeCache.class
.getMethod("removeAll", new Class[0]);
setUpCacheAsMockObject(removeAll);
expectCacheIsFound();
// cache manager throws exception when flushing the cache.
RuntimeException expected = new RuntimeException();
cache.removeAll();
cacheControl.setThrowable(expected);
replay();
try {
jcsFacade.onFlushCache(flushingModel);
fail();
} catch (CacheAccessException exception) {
assertSame(expected, exception.getCause());
}
verify();
}
public void testOnFlushCacheWhenCacheIsNotFound() throws Exception {
Method getCacheMethod = this
.getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCacheMethod);
expectCacheIsNotFound();
replay();
try {
jcsFacade.onFlushCache(flushingModel);
fail();
} catch (CacheAccessException exception) {
// expecting this exception
}
verify();
}
/**
* Verifies that the method
* <code>{@link JcsFacade#onFlushCache(org.springmodules.cache.FlushingModel)}</code>
* flushes only the specified groups of the specified cache.
*/
public void testOnFlushCacheWithGroups() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
Serializable[] keys = updateCache();
int entryToRemoveIndex = 0;
CacheEntry entryToRemove = cacheEntries[entryToRemoveIndex];
flushingModel.setCacheStruct(new CacheStruct(CACHE_NAME,
entryToRemove.group));
jcsFacade.onFlushCache(flushingModel);
int cacheElementCount = keys.length;
for (int i = 0; i < cacheElementCount; i++) {
if (i == entryToRemoveIndex) {
// the group of this element should have been removed.
assertNull(cache.get(keys[i]));
} else {
// the group of this element should exist.
assertNotNull(cache.get(keys[i]));
}
}
}
public void testOnFlushCacheWithoutCacheStructs() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
updateCache();
int expected = cache.getSize();
flushingModel.setCacheStruct(null);
jcsFacade.onFlushCache(flushingModel);
// the cache should not be flushed.
assertEquals(expected, cache.getSize());
}
/**
* Verifies that the method
* <code>{@link JcsFacade#onFlushCache(org.springmodules.cache.FlushingModel)}</code>
* flushes the whole cache if there are not any specified groups.
*/
public void testOnFlushCacheWithoutGroups() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
updateCache();
jcsFacade.onFlushCache(flushingModel);
// the whole cache should be flushed.
assertEquals(0, cache.getSize());
}
public void testOnGetFromCache() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
CacheEntry entryToAdd = cacheEntries[0];
updateCache(entryToAdd);
cachingModel.setGroup(entryToAdd.group);
Object actual = jcsFacade.onGetFromCache(entryToAdd.key, cachingModel);
assertSame(entryToAdd.value, actual);
}
public void testOnGetFromCacheWhenCacheAccessThrowsException()
throws Exception {
Method getCache = getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCache);
Method get = CompositeCache.class.getMethod("get",
new Class[] { Serializable.class });
setUpCacheAsMockObject(get);
expectCacheIsFound();
// cache manager throws exception.
Serializable key = "R2-D2";
RuntimeException expected = new RuntimeException();
cacheControl.expectAndThrow(cache.get(key), expected);
replay();
try {
jcsFacade.onGetFromCache(key, cachingModel);
fail();
} catch (CacheAccessException exception) {
assertSame(expected, exception.getCause());
}
verify();
}
public void testOnGetFromCacheWhenCacheIsNotFound() throws Exception {
Method getCacheMethod = this
.getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCacheMethod);
expectCacheIsNotFound();
replay();
try {
jcsFacade.onGetFromCache("C-3PO", cachingModel);
fail();
} catch (CacheNotFoundException exception) {
// expecting this exception
}
verify();
}
/**
* Verifies that the method
* <code>{@link JcsFacade#onGetFromCache(Serializable, org.springmodules.cache.CachingModel)}</code>
* returns <code>null</code> if the specified key does not exist in the
* cache.
*/
public void testOnGetFromCacheWhenKeyIsNotFound() throws Exception {
setUpCacheAdministratorAndCache();
assertNull(jcsFacade.onGetFromCache("SomeNotExistingKey", cachingModel));
}
public void testOnPutInCache() throws Exception {
setUpCacheAdministratorAndCache();
Serializable key = "Obi-Wan";
Object expected = "Jedi Master";
jcsFacade.onPutInCache(key, cachingModel, expected);
assertSame(expected, cache.get(key).getVal());
}
public void testOnPutInCacheWhenCacheAccessThrowsException() throws Exception {
Method getCache = getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCache);
Method update = CompositeCache.class.getMethod("update",
new Class[] { ICacheElement.class });
setUpCacheAsMockObject(update);
// cache manager finds the cache we are looking for.
expectCacheIsFound();
// cache manager throws exception.
Serializable key = "Jabba";
Object objToStore = "Bobba Fett";
CacheElement cacheElement = new CacheElement(cachingModel.getCacheName(),
key, objToStore);
cache.update(cacheElement);
cacheControl.setMatcher(new CacheElementMatcher());
RuntimeException expected = new RuntimeException();
cacheControl.setThrowable(expected);
replay();
try {
jcsFacade.onPutInCache(key, cachingModel, objToStore);
fail();
} catch (CacheAccessException exception) {
assertSame(expected, exception.getCause());
}
verify();
}
public void testOnPutInCacheWhenCacheIsNotFound() throws Exception {
Method getCache = getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCache);
expectCacheIsNotFound();
replay();
try {
jcsFacade.onPutInCache("Skywalker", cachingModel, "Luke");
fail();
} catch (CacheNotFoundException exception) {
// expecting this exception
}
verify();
}
public void testOnRemoveFromCache() throws Exception {
setUpCacheAdministratorAndCache();
setUpCacheEntries();
CacheEntry entry = cacheEntries[0];
Serializable key = updateCache(entry);
cachingModel.setGroup(entry.group);
jcsFacade.onRemoveFromCache(entry.key, cachingModel);
ICacheElement cacheElement = cache.get(key);
assertNull(cacheElement);
}
public void testOnRemoveFromCacheWhenCacheAccessThrowsException()
throws Exception {
Method getCacheMethod = this
.getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCacheMethod);
Method removeMethod = CompositeCache.class.getMethod("remove",
new Class[] { Serializable.class });
setUpCacheAsMockObject(removeMethod);
expectCacheIsFound();
// cache manager throws exception.
Serializable key = "FalconMillenium";
RuntimeException expected = new RuntimeException();
cache.remove(key);
cacheControl.setThrowable(expected);
replay();
try {
jcsFacade.onRemoveFromCache(key, cachingModel);
fail();
} catch (CacheAccessException exception) {
assertSame(expected, exception.getCause());
}
verify();
}
public void testOnRemoveFromCacheWhenCacheIsNotFound() throws Exception {
Method getCache = getGetCacheMethodFromCompositeCacheManagerClass();
setUpCacheAdministratorAsMockObject(getCache);
expectCacheIsNotFound();
replay();
try {
jcsFacade.onRemoveFromCache("Chewbacca", cachingModel);
fail();
} catch (CacheNotFoundException exception) {
// expecting this exception
}
verify();
}
/**
* Verifies that the method
* <code>{@link JcsFacade#validateCacheManager()}</code> throws an
* <code>{@link FatalCacheException}</code> the cache manager is
* <code>null</code>.
*/
public void testValidateCacheManagerWithCacheManagerEqualToNull() {
jcsFacade.setCacheManager(null);
try {
jcsFacade.validateCacheManager();
fail();
} catch (FatalCacheException exception) {
// we are expecting this exception.
}
}
/**
* Verifies that the method
* <code>{@link JcsFacade#validateCacheManager()}</code> does not throw any
* exception if the cache manager is not <code>null</code>.
*/
public void testValidateCacheManagerWithCacheManagerNotEqualToNull()
throws Exception {
setUpCacheAdministratorAndCache();
jcsFacade.validateCacheManager();
}
protected void setUp() {
cachingModel = new JcsCachingModel(CACHE_NAME);
flushingModel = new JcsFlushingModel(CACHE_NAME);
jcsFacade = new JcsFacade();
}
protected void tearDown() {
if (cacheManager != null) {
cacheManager.shutDown();
}
}
private void expectCacheIsFound() {
cacheManager.getCache(CACHE_NAME);
cacheManagerControl.setReturnValue(cache);
}
private void expectCacheIsNotFound() {
cacheManager.getCache(CACHE_NAME);
cacheManagerControl.setReturnValue(null);
}
private Method getGetCacheMethodFromCompositeCacheManagerClass()
throws Exception {
return CompositeCacheManager.class.getDeclaredMethod("getCache",
new Class[] { String.class });
}
private void replay() {
replay(cacheControl);
cacheManagerControl.replay();
}
private void replay(MockControl mockControl) {
if (mockControl == null) {
return;
}
mockControl.replay();
}
private void setUpCacheAdministratorAndCache() {
cacheManager = CompositeCacheManager.getInstance();
cache = cacheManager.getCache(CACHE_NAME);
jcsFacade.setCacheManager(cacheManager);
}
private void setUpCacheAdministratorAsMockObject(Method methodToMock) {
setUpCacheAdministratorAsMockObject(new Method[] { methodToMock });
}
private void setUpCacheAdministratorAsMockObject(Method[] methodsToMock) {
Class targetClass = CompositeCacheManager.class;
cacheManagerControl = MockClassControl.createControl(targetClass, null,
null, methodsToMock);
cacheManager = (CompositeCacheManager) cacheManagerControl.getMock();
jcsFacade.setCacheManager(cacheManager);
}
private void setUpCacheAsMockObject(Method methodToMock) throws Exception {
setUpCacheAsMockObject(new Method[] { methodToMock });
}
private void setUpCacheAsMockObject(Method[] methodsToMock) throws Exception {
Class[] constructorTypes = new Class[] { String.class,
ICompositeCacheAttributes.class, IElementAttributes.class };
ICompositeCacheAttributes cacheAttributes = new CompositeCacheAttributes();
cacheAttributes.setCacheName(CACHE_NAME);
cacheAttributes.setMaxObjects(10);
cacheAttributes
.setMemoryCacheName("org.apache.jcs.engine.memory.lru.LRUMemoryCache");
ElementAttributes elementAttributes = new ElementAttributes();
Object[] constructorArgs = new Object[] { CACHE_NAME, cacheAttributes,
elementAttributes };
// set up the methods to mock.
Class targetClass = CompositeCache.class;
cacheControl = MockClassControl.createControl(targetClass,
constructorTypes, constructorArgs, methodsToMock);
cache = (CompositeCache) cacheControl.getMock();
}
private void setUpCacheEntries() {
cacheEntries = new CacheEntry[] {
new CacheEntry("sith", "empire", "Darth Vader"),
new CacheEntry("jedi", "rebellion", "Luke Skywalker") };
}
private Serializable[] updateCache() throws Exception {
int elementCount = cacheEntries.length;
Serializable[] keys = new Serializable[elementCount];
for (int i = 0; i < elementCount; i++) {
keys[i] = updateCache(cacheEntries[i]);
}
assertTrue("The size of the cache should be greater than zero", cache
.getSize() > 0);
return keys;
}
private Serializable updateCache(CacheEntry cacheEntry) throws Exception {
Serializable key = cacheEntry.key;
String group = cacheEntry.group;
if (StringUtils.hasText(group)) {
GroupId groupId = new GroupId(CACHE_NAME, group);
GroupAttrName groupAttrName = new GroupAttrName(groupId, key);
key = groupAttrName;
}
ICacheElement cacheElement = new CacheElement(CACHE_NAME, key,
cacheEntry.value);
IElementAttributes attributes = cache.getElementAttributes().copy();
cacheElement.setElementAttributes(attributes);
cache.update(cacheElement);
return key;
}
private void verify() {
verify(cacheControl);
cacheManagerControl.verify();
}
private void verify(MockControl mockControl) {
if (mockControl == null) {
return;
}
mockControl.verify();
}
}